diff --git a/vm/src/dictdatatype.rs b/vm/src/dictdatatype.rs index 5541f17583..43a9fceb04 100644 --- a/vm/src/dictdatatype.rs +++ b/vm/src/dictdatatype.rs @@ -1,6 +1,6 @@ use crate::obj::objstr::PyString; use crate::pyhash; -use crate::pyobject::{IdProtocol, IntoPyObject, PyObjectRef, PyResult}; +use crate::pyobject::{IdProtocol, IntoPyObject, PyObjectRef, PyResult, ThreadSafe}; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; /// Ordered dictionary implementation. @@ -10,6 +10,7 @@ use num_bigint::ToBigInt; use std::collections::{hash_map::DefaultHasher, HashMap}; use std::hash::{Hash, Hasher}; use std::mem::size_of; +use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; /// hash value of an object returned by __hash__ type HashValue = pyhash::PyHash; @@ -18,30 +19,64 @@ type HashIndex = pyhash::PyHash; /// entry index mapped in indices type EntryIndex = usize; -#[derive(Clone)] pub struct Dict { + inner: RwLock>, +} + +impl ThreadSafe for Dict {} + +struct InnerDict { size: usize, indices: HashMap, entries: Vec>>, } +impl Clone for InnerDict { + fn clone(&self) -> Self { + InnerDict { + size: self.size, + indices: self.indices.clone(), + entries: self.entries.clone(), + } + } +} + +impl Clone for Dict { + fn clone(&self) -> Self { + Dict { + inner: RwLock::new(self.inner.read().unwrap().clone()), + } + } +} + impl Default for Dict { fn default() -> Self { Dict { - size: 0, - indices: HashMap::new(), - entries: Vec::new(), + inner: RwLock::new(InnerDict { + size: 0, + indices: HashMap::new(), + entries: Vec::new(), + }), } } } -#[derive(Clone)] struct DictEntry { hash: HashValue, key: PyObjectRef, value: T, } +impl Clone for DictEntry { + fn clone(&self) -> Self { + DictEntry { + hash: self.hash, + key: self.key.clone(), + value: self.value.clone(), + } + } +} + #[derive(Debug)] pub struct DictSize { size: usize, @@ -49,10 +84,19 @@ pub struct DictSize { } impl Dict { - fn resize(&mut self) { - let mut new_indices = HashMap::with_capacity(self.size); - let mut new_entries = Vec::with_capacity(self.size); - for maybe_entry in self.entries.drain(0..) { + fn borrow_value(&self) -> RwLockReadGuard<'_, InnerDict> { + self.inner.read().unwrap() + } + + fn borrow_value_mut(&self) -> RwLockWriteGuard<'_, InnerDict> { + self.inner.write().unwrap() + } + + fn resize(&self) { + let mut inner = self.borrow_value_mut(); + let mut new_indices = HashMap::with_capacity(inner.size); + let mut new_entries = Vec::with_capacity(inner.size); + for maybe_entry in inner.entries.drain(0..) { if let Some(entry) = maybe_entry { let mut hash_index = entry.hash; // Faster version of lookup. No equality checks here. @@ -64,12 +108,12 @@ impl Dict { new_entries.push(Some(entry)); } } - self.indices = new_indices; - self.entries = new_entries; + inner.indices = new_indices; + inner.entries = new_entries; } fn unchecked_push( - &mut self, + &self, hash_index: HashIndex, hash_value: HashValue, key: PyObjectRef, @@ -80,44 +124,46 @@ impl Dict { key, value, }; - let entry_index = self.entries.len(); - self.entries.push(Some(entry)); - self.indices.insert(hash_index, entry_index); - self.size += 1; - } - - fn unchecked_delete(&mut self, entry_index: EntryIndex) { - self.entries[entry_index] = None; - self.size -= 1; + let mut inner = self.borrow_value_mut(); + let entry_index = inner.entries.len(); + inner.entries.push(Some(entry)); + inner.indices.insert(hash_index, entry_index); + inner.size += 1; } /// Store a key pub fn insert( - &mut self, + &self, vm: &VirtualMachine, key: K, value: T, ) -> PyResult<()> { - if self.indices.len() > 2 * self.size { + // This does not need to be accurate so we can take the lock mutiple times. + if self.borrow_value().indices.len() > 2 * self.borrow_value().size { self.resize(); } - match self.lookup(vm, key)? { - LookupResult::Existing(index) => { - // Update existing key - if let Some(ref mut entry) = self.entries[index] { - entry.value = value; - Ok(()) - } else { - panic!("Lookup returned invalid index into entries!"); + loop { + match self.lookup(vm, key)? { + LookupResult::Existing(index) => { + let mut inner = self.borrow_value_mut(); + // Update existing key + if let Some(ref mut entry) = inner.entries[index] { + // They entry might have changed since we did lookup. Should we update the key? + entry.value = value; + break Ok(()); + } else { + // The dict was changed since we did lookup. Let's try again. + continue; + } + } + LookupResult::NewIndex { + hash_index, + hash_value, + } => { + // New key: + self.unchecked_push(hash_index, hash_value, key.into_pyobject(vm)?, value); + break Ok(()); } - } - LookupResult::NewIndex { - hash_index, - hash_value, - } => { - // New key: - self.unchecked_push(hash_index, hash_value, key.into_pyobject(vm)?, value); - Ok(()) } } } @@ -130,32 +176,32 @@ impl Dict { } } - fn unchecked_get(&self, index: EntryIndex) -> T { - if let Some(entry) = &self.entries[index] { - entry.value.clone() - } else { - panic!("Lookup returned invalid index into entries!"); - } - } - /// Retrieve a key #[cfg_attr(feature = "flame-it", flame("Dict"))] pub fn get(&self, vm: &VirtualMachine, key: K) -> PyResult> { - if let LookupResult::Existing(index) = self.lookup(vm, key)? { - Ok(Some(self.unchecked_get(index))) - } else { - Ok(None) + loop { + if let LookupResult::Existing(index) = self.lookup(vm, key)? { + if let Some(entry) = &self.borrow_value().entries[index] { + break Ok(Some(entry.value.clone())); + } else { + // The dict was changed since we did lookup. Let's try again. + continue; + } + } else { + break Ok(None); + } } } - pub fn clear(&mut self) { - self.entries.clear(); - self.indices.clear(); - self.size = 0 + pub fn clear(&self) { + let mut inner = self.borrow_value_mut(); + inner.entries.clear(); + inner.indices.clear(); + inner.size = 0 } /// Delete a key - pub fn delete(&mut self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<()> { + pub fn delete(&self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<()> { if self.delete_if_exists(vm, key)? { Ok(()) } else { @@ -163,33 +209,56 @@ impl Dict { } } - pub fn delete_if_exists(&mut self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult { - if let LookupResult::Existing(entry_index) = self.lookup(vm, key)? { - self.unchecked_delete(entry_index); - Ok(true) - } else { - Ok(false) + pub fn delete_if_exists(&self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult { + loop { + if let LookupResult::Existing(entry_index) = self.lookup(vm, key)? { + let mut inner = self.borrow_value_mut(); + if inner.entries[entry_index].is_some() { + inner.entries[entry_index] = None; + inner.size -= 1; + break Ok(true); + } else { + // The dict was changed since we did lookup. Let's try again. + continue; + } + } else { + break Ok(false); + } } } pub fn delete_or_insert( - &mut self, + &self, vm: &VirtualMachine, key: &PyObjectRef, value: T, ) -> PyResult<()> { - match self.lookup(vm, key)? { - LookupResult::Existing(entry_index) => self.unchecked_delete(entry_index), - LookupResult::NewIndex { - hash_value, - hash_index, - } => self.unchecked_push(hash_index, hash_value, key.clone(), value), - }; - Ok(()) + loop { + match self.lookup(vm, key)? { + LookupResult::Existing(entry_index) => { + let mut inner = self.borrow_value_mut(); + if inner.entries[entry_index].is_some() { + inner.entries[entry_index] = None; + inner.size -= 1; + break Ok(()); + } else { + // The dict was changed since we did lookup. Let's try again. + continue; + } + } + LookupResult::NewIndex { + hash_value, + hash_index, + } => { + self.unchecked_push(hash_index, hash_value, key.clone(), value); + break Ok(()); + } + }; + } } pub fn len(&self) -> usize { - self.size + self.borrow_value().size } pub fn is_empty(&self) -> bool { @@ -197,33 +266,42 @@ impl Dict { } pub fn size(&self) -> DictSize { + let inner = self.borrow_value(); DictSize { - size: self.size, - entries_size: self.entries.len(), + size: inner.size, + entries_size: inner.entries.len(), } } - pub fn next_entry(&self, position: &mut EntryIndex) -> Option<(&PyObjectRef, &T)> { - self.entries[*position..].iter().find_map(|entry| { - *position += 1; - entry - .as_ref() - .map(|DictEntry { key, value, .. }| (key, value)) - }) + pub fn next_entry(&self, position: &mut EntryIndex) -> Option<(PyObjectRef, T)> { + self.borrow_value().entries[*position..] + .iter() + .find_map(|entry| { + *position += 1; + entry + .as_ref() + .map(|DictEntry { key, value, .. }| (key.clone(), value.clone())) + }) } pub fn len_from_entry_index(&self, position: EntryIndex) -> usize { - self.entries[position..].iter().flatten().count() + self.borrow_value().entries[position..] + .iter() + .flatten() + .count() } pub fn has_changed_size(&self, position: &DictSize) -> bool { - position.size != self.size || self.entries.len() != position.entries_size + let inner = self.borrow_value(); + position.size != inner.size || inner.entries.len() != position.entries_size } - pub fn keys<'a>(&'a self) -> impl Iterator + 'a { - self.entries + pub fn keys(&self) -> Vec { + self.borrow_value() + .entries .iter() .filter_map(|v| v.as_ref().map(|v| v.key.clone())) + .collect() } /// Lookup the index for the given key. @@ -232,33 +310,41 @@ impl Dict { let hash_value = key.do_hash(vm)?; let perturb = hash_value; let mut hash_index: HashIndex = hash_value; - loop { - if self.indices.contains_key(&hash_index) { - // Now we have an index, lets check the key. - let index = self.indices[&hash_index]; - if let Some(entry) = &self.entries[index] { - // Okay, we have an entry at this place - if key.do_is(&entry.key) { - // Literally the same object - break Ok(LookupResult::Existing(index)); - } else if entry.hash == hash_value { - if key.do_eq(vm, &entry.key)? { - break Ok(LookupResult::Existing(index)); + 'outer: loop { + let (entry, index) = loop { + let inner = self.borrow_value(); + if inner.indices.contains_key(&hash_index) { + // Now we have an index, lets check the key. + let index = inner.indices[&hash_index]; + if let Some(entry) = &inner.entries[index] { + // Okay, we have an entry at this place + if key.do_is(&entry.key) { + // Literally the same object + break 'outer Ok(LookupResult::Existing(index)); + } else if entry.hash == hash_value { + break (entry.clone(), index); } else { // entry mismatch. } } else { - // entry mismatch. + // Removed entry, continue search... } } else { - // Removed entry, continue search... + // Hash not in table, we are at free slot now. + break 'outer Ok(LookupResult::NewIndex { + hash_value, + hash_index, + }); } + // Update i to next probe location: + hash_index = Self::next_index(perturb, hash_index) + // warn!("Perturb value: {}", i); + }; + // This comparison needs to be done outside the lock. + if key.do_eq(vm, &entry.key)? { + break Ok(LookupResult::Existing(index)); } else { - // Hash not in table, we are at free slot now. - break Ok(LookupResult::NewIndex { - hash_value, - hash_index, - }); + // entry mismatch. } // Update i to next probe location: @@ -275,30 +361,45 @@ impl Dict { } /// Retrieve and delete a key - pub fn pop(&mut self, vm: &VirtualMachine, key: K) -> PyResult> { - if let LookupResult::Existing(index) = self.lookup(vm, key)? { - let value = self.unchecked_get(index); - self.unchecked_delete(index); - Ok(Some(value)) - } else { - Ok(None) + pub fn pop(&self, vm: &VirtualMachine, key: K) -> PyResult> { + loop { + if let LookupResult::Existing(index) = self.lookup(vm, key)? { + let mut inner = self.borrow_value_mut(); + if let Some(entry) = &inner.entries[index] { + let value = entry.value.clone(); + inner.entries[index] = None; + inner.size -= 1; + break Ok(Some(value)); + } else { + // The dict was changed since we did lookup. Let's try again. + continue; + } + } else { + break Ok(None); + } } } - pub fn pop_front(&mut self) -> Option<(PyObjectRef, T)> { - let mut entry_index = 0; - match self.next_entry(&mut entry_index) { - Some((key, value)) => { - let item = (key.clone(), value.clone()); - self.unchecked_delete(entry_index - 1); - Some(item) - } - None => None, + pub fn pop_front(&self) -> Option<(PyObjectRef, T)> { + let mut position = 0; + let mut inner = self.borrow_value_mut(); + let first_item = inner.entries.iter().find_map(|entry| { + position += 1; + entry + .as_ref() + .map(|DictEntry { key, value, .. }| (key.clone(), value.clone())) + }); + if let Some(item) = first_item { + inner.entries[position - 1] = None; + inner.size -= 1; + Some(item) + } else { + None } } pub fn sizeof(&self) -> usize { - size_of::() + self.size * size_of::>() + size_of::() + self.borrow_value().size * size_of::>() } } @@ -389,7 +490,7 @@ mod tests { #[test] fn test_insert() { let vm: VirtualMachine = Default::default(); - let mut dict = Dict::default(); + let dict = Dict::default(); assert_eq!(0, dict.len()); let key1 = vm.new_bool(true); diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 0573253b94..a5330bdbcf 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -1,4 +1,4 @@ -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::fmt; use super::objiter; @@ -9,7 +9,7 @@ use crate::exceptions::PyBaseExceptionRef; use crate::function::{KwArgs, OptionalArg, PyFuncArgs}; use crate::pyobject::{ IdProtocol, IntoPyObject, ItemProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable, - PyObjectRef, PyRef, PyResult, PyValue, + PyObjectRef, PyRef, PyResult, PyValue, ThreadSafe, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -20,9 +20,10 @@ pub type DictContentType = dictdatatype::Dict; #[pyclass] #[derive(Default)] pub struct PyDict { - entries: RefCell, + entries: DictContentType, } pub type PyDictRef = PyRef; +impl ThreadSafe for PyDict {} impl fmt::Debug for PyDict { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -43,7 +44,7 @@ impl PyDictRef { #[pyslot] fn tp_new(class: PyClassRef, _args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { PyDict { - entries: RefCell::new(DictContentType::default()), + entries: DictContentType::default(), } .into_ref_with_type(vm, class) } @@ -59,7 +60,7 @@ impl PyDictRef { } fn merge( - dict: &RefCell, + dict: &DictContentType, dict_obj: OptionalArg, kwargs: KwArgs, vm: &VirtualMachine, @@ -68,13 +69,13 @@ impl PyDictRef { let dicted: Result = dict_obj.clone().downcast(); if let Ok(dict_obj) = dicted { for (key, value) in dict_obj { - dict.borrow_mut().insert(vm, &key, value)?; + dict.insert(vm, &key, value)?; } } else if let Some(keys) = vm.get_method(dict_obj.clone(), "keys") { let keys = objiter::get_iter(vm, &vm.invoke(&keys?, vec![])?)?; while let Some(key) = objiter::get_next_object(vm, &keys)? { let val = dict_obj.get_item(&key, vm)?; - dict.borrow_mut().insert(vm, &key, val)?; + dict.insert(vm, &key, val)?; } } else { let iter = objiter::get_iter(vm, &dict_obj)?; @@ -92,14 +93,13 @@ impl PyDictRef { if objiter::get_next_object(vm, &elem_iter)?.is_some() { return Err(err(vm)); } - dict.borrow_mut().insert(vm, &key, value)?; + dict.insert(vm, &key, value)?; } } } - let mut dict_borrowed = dict.borrow_mut(); for (key, value) in kwargs.into_iter() { - dict_borrowed.insert(vm, &vm.new_str(key), value)?; + dict.insert(vm, &vm.new_str(key), value)?; } Ok(()) } @@ -111,27 +111,26 @@ impl PyDictRef { value: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - let mut dict = DictContentType::default(); + let dict = DictContentType::default(); let value = value.unwrap_or_else(|| vm.ctx.none()); for elem in iterable.iter(vm)? { let elem = elem?; dict.insert(vm, &elem, value.clone())?; } - let entries = RefCell::new(dict); - PyDict { entries }.into_ref_with_type(vm, class) + PyDict { entries: dict }.into_ref_with_type(vm, class) } #[pymethod(magic)] fn bool(self) -> bool { - !self.entries.borrow().is_empty() + !self.entries.is_empty() } fn inner_eq(self, other: &PyDict, vm: &VirtualMachine) -> PyResult { - if other.entries.borrow().len() != self.entries.borrow().len() { + if other.entries.len() != self.entries.len() { return Ok(false); } for (k, v1) in self { - match other.entries.borrow().get(vm, &k)? { + match other.entries.get(vm, &k)? { Some(v2) => { if v1.is(&v2) { continue; @@ -170,12 +169,12 @@ impl PyDictRef { #[pymethod(magic)] fn len(self) -> usize { - self.entries.borrow().len() + self.entries.len() } #[pymethod(magic)] fn sizeof(self) -> usize { - size_of::() + self.entries.borrow().sizeof() + size_of::() + self.entries.sizeof() } #[pymethod(magic)] @@ -197,17 +196,17 @@ impl PyDictRef { #[pymethod(magic)] fn contains(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.entries.borrow().contains(vm, &key) + self.entries.contains(vm, &key) } #[pymethod(magic)] fn delitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - self.entries.borrow_mut().delete(vm, &key) + self.entries.delete(vm, &key) } #[pymethod] fn clear(self) { - self.entries.borrow_mut().clear() + self.entries.clear() } #[pymethod(magic)] @@ -243,7 +242,7 @@ impl PyDictRef { value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - self.entries.borrow_mut().insert(vm, key, value) + self.entries.insert(vm, key, value) } #[pymethod(magic)] @@ -262,7 +261,7 @@ impl PyDictRef { key: K, vm: &VirtualMachine, ) -> PyResult> { - if let Some(value) = self.entries.borrow().get(vm, key)? { + if let Some(value) = self.entries.get(vm, key)? { return Ok(Some(value)); } @@ -281,7 +280,7 @@ impl PyDictRef { default: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - match self.entries.borrow().get(vm, &key)? { + match self.entries.get(vm, &key)? { Some(value) => Ok(value), None => Ok(default.unwrap_or_else(|| vm.ctx.none())), } @@ -294,12 +293,11 @@ impl PyDictRef { default: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - let mut entries = self.entries.borrow_mut(); - match entries.get(vm, &key)? { + match self.entries.get(vm, &key)? { Some(value) => Ok(value), None => { let set_value = default.unwrap_or_else(|| vm.ctx.none()); - entries.insert(vm, &key, set_value.clone())?; + self.entries.insert(vm, &key, set_value.clone())?; Ok(set_value) } } @@ -329,7 +327,7 @@ impl PyDictRef { default: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - match self.entries.borrow_mut().pop(vm, &key)? { + match self.entries.pop(vm, &key)? { Some(value) => Ok(value), None => match default { OptionalArg::Present(default) => Ok(default), @@ -340,8 +338,7 @@ impl PyDictRef { #[pymethod] fn popitem(self, vm: &VirtualMachine) -> PyResult { - let mut entries = self.entries.borrow_mut(); - if let Some((key, value)) = entries.pop_front() { + if let Some((key, value)) = self.entries.pop_front() { Ok(vm.ctx.new_tuple(vec![key, value])) } else { let err_msg = vm.new_str("popitem(): dictionary is empty".to_owned()); @@ -360,14 +357,13 @@ impl PyDictRef { } pub fn from_attributes(attrs: PyAttributes, vm: &VirtualMachine) -> PyResult { - let mut dict = DictContentType::default(); + let dict = DictContentType::default(); for (key, value) in attrs { dict.insert(vm, &vm.ctx.new_str(key), value)?; } - let entries = RefCell::new(dict); - Ok(PyDict { entries }.into_ref(vm)) + Ok(PyDict { entries: dict }.into_ref(vm)) } #[pymethod(magic)] @@ -377,11 +373,11 @@ impl PyDictRef { pub fn contains_key(&self, key: T, vm: &VirtualMachine) -> bool { let key = key.into_pyobject(vm).unwrap(); - self.entries.borrow().contains(vm, &key).unwrap() + self.entries.contains(vm, &key).unwrap() } pub fn size(&self) -> dictdatatype::DictSize { - self.entries.borrow().size() + self.entries.size() } /// This function can be used to get an item without raising the @@ -487,8 +483,8 @@ impl Iterator for DictIter { type Item = (PyObjectRef, PyObjectRef); fn next(&mut self) -> Option { - match self.dict.entries.borrow().next_entry(&mut self.position) { - Some((key, value)) => Some((key.clone(), value.clone())), + match self.dict.entries.next_entry(&mut self.position) { + Some((key, value)) => Some((key, value)), None => None, } } @@ -524,7 +520,7 @@ macro_rules! dict_iterator { let s = if let Some(_guard) = ReprGuard::enter(zelf.as_object()) { let mut str_parts = vec![]; for (key, value) in zelf.dict.clone() { - let s = vm.to_repr(&$result_fn(vm, &key, &value))?; + let s = vm.to_repr(&$result_fn(vm, key, value))?; str_parts.push(s.as_str().to_owned()); } format!("{}([{}])", $class_name, str_parts.join(", ")) @@ -563,13 +559,12 @@ macro_rules! dict_iterator { #[allow(clippy::redundant_closure_call)] fn next(&self, vm: &VirtualMachine) -> PyResult { let mut position = self.position.get(); - let dict = self.dict.entries.borrow(); - if dict.has_changed_size(&self.size) { + if self.dict.entries.has_changed_size(&self.size) { return Err( vm.new_runtime_error("dictionary changed size during iteration".to_owned()) ); } - match dict.next_entry(&mut position) { + match self.dict.entries.next_entry(&mut position) { Some((key, value)) => { self.position.set(position); Ok($result_fn(vm, key, value)) @@ -585,10 +580,7 @@ macro_rules! dict_iterator { #[pymethod(name = "__length_hint__")] fn length_hint(&self) -> usize { - self.dict - .entries - .borrow() - .len_from_entry_index(self.position.get()) + self.dict.entries.len_from_entry_index(self.position.get()) } } @@ -607,7 +599,7 @@ dict_iterator! { dictkeyiterator_type, "dict_keys", "dictkeyiterator", - |_vm: &VirtualMachine, key: &PyObjectRef, _value: &PyObjectRef| key.clone() + |_vm: &VirtualMachine, key: PyObjectRef, _value: PyObjectRef| key } dict_iterator! { @@ -617,7 +609,7 @@ dict_iterator! { dictvalueiterator_type, "dict_values", "dictvalueiterator", - |_vm: &VirtualMachine, _key: &PyObjectRef, value: &PyObjectRef| value.clone() + |_vm: &VirtualMachine, _key: PyObjectRef, value: PyObjectRef| value } dict_iterator! { @@ -627,8 +619,8 @@ dict_iterator! { dictitemiterator_type, "dict_items", "dictitemiterator", - |vm: &VirtualMachine, key: &PyObjectRef, value: &PyObjectRef| - vm.ctx.new_tuple(vec![key.clone(), value.clone()]) + |vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef| + vm.ctx.new_tuple(vec![key, value]) } pub(crate) fn init(context: &PyContext) { diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 7e65da207a..f467241177 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -1,8 +1,6 @@ /* * Builtin set type with a sequence of unique items. */ - -use std::cell::RefCell; use std::fmt; use super::objlist::PyListIterator; @@ -11,8 +9,8 @@ use crate::dictdatatype; use crate::function::{Args, OptionalArg}; use crate::pyhash; use crate::pyobject::{ - PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, - TypeProtocol, + PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, ThreadSafe, + TryFromObject, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -25,9 +23,10 @@ pub type SetContentType = dictdatatype::Dict<()>; #[pyclass] #[derive(Default)] pub struct PySet { - inner: RefCell, + inner: PySetInner, } pub type PySetRef = PyRef; +impl ThreadSafe for PySet {} /// frozenset() -> empty frozenset object /// frozenset(iterable) -> frozenset object @@ -39,6 +38,7 @@ pub struct PyFrozenSet { inner: PySetInner, } pub type PyFrozenSetRef = PyRef; +impl ThreadSafe for PyFrozenSet {} impl fmt::Debug for PySet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -73,7 +73,7 @@ struct PySetInner { impl PySetInner { fn new(iterable: PyIterable, vm: &VirtualMachine) -> PyResult { - let mut set = PySetInner::default(); + let set = PySetInner::default(); for item in iterable.iter(vm)? { set.add(&item?, vm)?; } @@ -177,7 +177,7 @@ impl PySetInner { } fn union(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { - let mut set = self.clone(); + let set = self.clone(); for item in other.iter(vm)? { set.add(&item?, vm)?; } @@ -186,7 +186,7 @@ impl PySetInner { } fn intersection(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { - let mut set = PySetInner::default(); + let set = PySetInner::default(); for item in other.iter(vm)? { let obj = item?; if self.contains(&obj, vm)? { @@ -197,7 +197,7 @@ impl PySetInner { } fn difference(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { - let mut set = self.copy(); + let set = self.copy(); for item in other.iter(vm)? { set.content.delete_if_exists(vm, &item?)?; } @@ -205,7 +205,7 @@ impl PySetInner { } fn symmetric_difference(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { - let mut new_inner = self.clone(); + let new_inner = self.clone(); // We want to remove duplicates in other let other_set = Self::new(other, vm)?; @@ -241,8 +241,7 @@ impl PySetInner { } fn iter(&self, vm: &VirtualMachine) -> PyListIterator { - let items = self.content.keys().collect(); - let set_list = vm.ctx.new_list(items); + let set_list = vm.ctx.new_list(self.content.keys()); PyListIterator { position: crossbeam_utils::atomic::AtomicCell::new(0), list: set_list.downcast().unwrap(), @@ -259,23 +258,23 @@ impl PySetInner { Ok(format!("{{{}}}", str_parts.join(", "))) } - fn add(&mut self, item: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + fn add(&self, item: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { self.content.insert(vm, item, ()) } - fn remove(&mut self, item: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + fn remove(&self, item: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { self.content.delete(vm, item) } - fn discard(&mut self, item: &PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn discard(&self, item: &PyObjectRef, vm: &VirtualMachine) -> PyResult { self.content.delete_if_exists(vm, item) } - fn clear(&mut self) { + fn clear(&self) { self.content.clear() } - fn pop(&mut self, vm: &VirtualMachine) -> PyResult { + fn pop(&self, vm: &VirtualMachine) -> PyResult { if let Some((key, _)) = self.content.pop_front() { Ok(key) } else { @@ -284,7 +283,7 @@ impl PySetInner { } } - fn update(&mut self, others: Args, vm: &VirtualMachine) -> PyResult<()> { + fn update(&self, others: Args, vm: &VirtualMachine) -> PyResult<()> { for iterable in others { for item in iterable.iter(vm)? { self.add(&item?, vm)?; @@ -293,11 +292,7 @@ impl PySetInner { Ok(()) } - fn intersection_update( - &mut self, - others: Args, - vm: &VirtualMachine, - ) -> PyResult<()> { + fn intersection_update(&self, others: Args, vm: &VirtualMachine) -> PyResult<()> { let mut temp_inner = self.copy(); self.clear(); for iterable in others { @@ -312,7 +307,7 @@ impl PySetInner { Ok(()) } - fn difference_update(&mut self, others: Args, vm: &VirtualMachine) -> PyResult<()> { + fn difference_update(&self, others: Args, vm: &VirtualMachine) -> PyResult<()> { for iterable in others { for item in iterable.iter(vm)? { self.content.delete_if_exists(vm, &item?)?; @@ -322,7 +317,7 @@ impl PySetInner { } fn symmetric_difference_update( - &mut self, + &self, others: Args, vm: &VirtualMachine, ) -> PyResult<()> { @@ -337,14 +332,14 @@ impl PySetInner { } fn hash(&self, vm: &VirtualMachine) -> PyResult { - pyhash::hash_iter_unordered(self.content.keys(), vm) + pyhash::hash_iter_unordered(self.content.keys().iter(), vm) } } macro_rules! try_set_cmp { ($vm:expr, $other:expr, $op:expr) => { Ok(match_class!(match ($other) { - set @ PySet => ($vm.new_bool($op(&*set.inner.borrow())?)), + set @ PySet => ($vm.new_bool($op(&set.inner)?)), frozen @ PyFrozenSet => ($vm.new_bool($op(&frozen.inner)?)), _ => $vm.ctx.not_implemented(), })); @@ -353,13 +348,11 @@ macro_rules! try_set_cmp { macro_rules! multi_args_set { ($vm:expr, $others:expr, $zelf:expr, $op:tt) => {{ - let mut res = $zelf.inner.borrow().copy(); + let mut res = $zelf.inner.copy(); for other in $others { res = res.$op(other, $vm)? } - Ok(Self { - inner: RefCell::new(res), - }) + Ok(Self { inner: res }) }}; } @@ -372,61 +365,61 @@ impl PySet { vm: &VirtualMachine, ) -> PyResult { Self { - inner: RefCell::new(PySetInner::from_arg(iterable, vm)?), + inner: PySetInner::from_arg(iterable, vm)?, } .into_ref_with_type(vm, cls) } #[pymethod(name = "__len__")] fn len(&self) -> usize { - self.inner.borrow().len() + self.inner.len() } #[pymethod(name = "__sizeof__")] fn sizeof(&self) -> usize { - std::mem::size_of::() + self.inner.borrow().sizeof() + std::mem::size_of::() + self.inner.sizeof() } #[pymethod] fn copy(&self) -> Self { Self { - inner: RefCell::new(self.inner.borrow().copy()), + inner: self.inner.copy(), } } #[pymethod(name = "__contains__")] fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.inner.borrow().contains(&needle, vm) + self.inner.contains(&needle, vm) } #[pymethod(name = "__eq__")] fn eq(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - try_set_cmp!(vm, other, |other| self.inner.borrow().eq(other, vm)) + try_set_cmp!(vm, other, |other| self.inner.eq(other, vm)) } #[pymethod(name = "__ne__")] fn ne(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - try_set_cmp!(vm, other, |other| self.inner.borrow().ne(other, vm)) + try_set_cmp!(vm, other, |other| self.inner.ne(other, vm)) } #[pymethod(name = "__ge__")] fn ge(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - try_set_cmp!(vm, other, |other| self.inner.borrow().ge(other, vm)) + try_set_cmp!(vm, other, |other| self.inner.ge(other, vm)) } #[pymethod(name = "__gt__")] fn gt(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - try_set_cmp!(vm, other, |other| self.inner.borrow().gt(other, vm)) + try_set_cmp!(vm, other, |other| self.inner.gt(other, vm)) } #[pymethod(name = "__le__")] fn le(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - try_set_cmp!(vm, other, |other| self.inner.borrow().le(other, vm)) + try_set_cmp!(vm, other, |other| self.inner.le(other, vm)) } #[pymethod(name = "__lt__")] fn lt(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - try_set_cmp!(vm, other, |other| self.inner.borrow().lt(other, vm)) + try_set_cmp!(vm, other, |other| self.inner.lt(other, vm)) } #[pymethod] @@ -455,17 +448,17 @@ impl PySet { #[pymethod] fn issubset(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { - self.inner.borrow().issubset(other, vm) + self.inner.issubset(other, vm) } #[pymethod] fn issuperset(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { - self.inner.borrow().issuperset(other, vm) + self.inner.issuperset(other, vm) } #[pymethod] fn isdisjoint(&self, other: PyIterable, vm: &VirtualMachine) -> PyResult { - self.inner.borrow().isdisjoint(other, vm) + self.inner.isdisjoint(other, vm) } #[pymethod(name = "__or__")] @@ -510,16 +503,15 @@ impl PySet { #[pymethod(name = "__iter__")] fn iter(&self, vm: &VirtualMachine) -> PyListIterator { - self.inner.borrow().iter(vm) + self.inner.iter(vm) } #[pymethod(name = "__repr__")] fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let inner = zelf.inner.borrow(); - let s = if inner.len() == 0 { + let s = if zelf.inner.len() == 0 { "set()".to_owned() } else if let Some(_guard) = ReprGuard::enter(zelf.as_object()) { - inner.repr(vm)? + zelf.inner.repr(vm)? } else { "set(...)".to_owned() }; @@ -528,68 +520,64 @@ impl PySet { #[pymethod] pub fn add(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - self.inner.borrow_mut().add(&item, vm)?; + self.inner.add(&item, vm)?; Ok(()) } #[pymethod] fn remove(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - self.inner.borrow_mut().remove(&item, vm) + self.inner.remove(&item, vm) } #[pymethod] fn discard(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - self.inner.borrow_mut().discard(&item, vm)?; + self.inner.discard(&item, vm)?; Ok(()) } #[pymethod] fn clear(&self) { - self.inner.borrow_mut().clear() + self.inner.clear() } #[pymethod] fn pop(&self, vm: &VirtualMachine) -> PyResult { - self.inner.borrow_mut().pop(vm) + self.inner.pop(vm) } #[pymethod(name = "__ior__")] fn ior(zelf: PyRef, iterable: SetIterable, vm: &VirtualMachine) -> PyResult { - zelf.inner.borrow_mut().update(iterable.iterable, vm)?; + zelf.inner.update(iterable.iterable, vm)?; Ok(zelf.as_object().clone()) } #[pymethod] fn update(&self, others: Args, vm: &VirtualMachine) -> PyResult { - self.inner.borrow_mut().update(others, vm)?; + self.inner.update(others, vm)?; Ok(vm.get_none()) } #[pymethod] fn intersection_update(&self, others: Args, vm: &VirtualMachine) -> PyResult { - self.inner.borrow_mut().intersection_update(others, vm)?; + self.inner.intersection_update(others, vm)?; Ok(vm.get_none()) } #[pymethod(name = "__iand__")] fn iand(zelf: PyRef, iterable: SetIterable, vm: &VirtualMachine) -> PyResult { - zelf.inner - .borrow_mut() - .intersection_update(iterable.iterable, vm)?; + zelf.inner.intersection_update(iterable.iterable, vm)?; Ok(zelf.as_object().clone()) } #[pymethod] fn difference_update(&self, others: Args, vm: &VirtualMachine) -> PyResult { - self.inner.borrow_mut().difference_update(others, vm)?; + self.inner.difference_update(others, vm)?; Ok(vm.get_none()) } #[pymethod(name = "__isub__")] fn isub(zelf: PyRef, iterable: SetIterable, vm: &VirtualMachine) -> PyResult { - zelf.inner - .borrow_mut() - .difference_update(iterable.iterable, vm)?; + zelf.inner.difference_update(iterable.iterable, vm)?; Ok(zelf.as_object().clone()) } @@ -599,16 +587,13 @@ impl PySet { others: Args, vm: &VirtualMachine, ) -> PyResult { - self.inner - .borrow_mut() - .symmetric_difference_update(others, vm)?; + self.inner.symmetric_difference_update(others, vm)?; Ok(vm.get_none()) } #[pymethod(name = "__ixor__")] fn ixor(zelf: PyRef, iterable: SetIterable, vm: &VirtualMachine) -> PyResult { zelf.inner - .borrow_mut() .symmetric_difference_update(iterable.iterable, vm)?; Ok(zelf.as_object().clone()) } @@ -635,7 +620,7 @@ impl PyFrozenSet { vm: &VirtualMachine, it: impl IntoIterator, ) -> PyResult { - let mut inner = PySetInner::default(); + let inner = PySetInner::default(); for elem in it { inner.add(&elem, vm)?; } diff --git a/vm/src/pyhash.rs b/vm/src/pyhash.rs index 7c650517b5..60a6d132e6 100644 --- a/vm/src/pyhash.rs +++ b/vm/src/pyhash.rs @@ -84,13 +84,13 @@ pub fn hash_iter<'a, I: std::iter::Iterator>( Ok(hasher.finish() as PyHash) } -pub fn hash_iter_unordered>( +pub fn hash_iter_unordered<'a, I: std::iter::Iterator>( iter: I, vm: &VirtualMachine, ) -> PyResult { let mut hash: PyHash = 0; for element in iter { - let item_hash = vm._hash(&element)?; + let item_hash = vm._hash(element)?; // xor is commutative and hash should be independent of order hash ^= item_hash; }