changed VData implementation. It will now always (exception: references) behave like a clone BUT it will avoid cloning whenever possible using Copy-on-Write-style logic.

This commit is contained in:
mark 2023-05-11 14:49:49 +02:00
parent 79b169d951
commit 6f31abd5cc

View File

@ -24,6 +24,8 @@ impl VData {
return; return;
} }
} }
#[cfg(debug_assertions)]
eprintln!("VData: assign: overwriting my previous Arc because it was immutable.");
*self = new_val.to(); *self = new_val.to();
} }
pub fn inner_replace(&mut self, new_val: VDataEnum) -> VDataEnum { pub fn inner_replace(&mut self, new_val: VDataEnum) -> VDataEnum {
@ -39,14 +41,20 @@ impl VData {
} }
/// returns the contained VDataEnum. May or may not clone. /// returns the contained VDataEnum. May or may not clone.
pub fn inner(self) -> VDataEnum { pub fn inner(self) -> VDataEnum {
self.data().0.clone() // Arc::unwrap_or_clone(self.data).lock().unwrap().0
let o = match Arc::try_unwrap(self.data) {
Ok(v) => std::mem::replace(&mut v.lock().unwrap().0, VDataEnum::Bool(false)),
Err(e) => e.lock().unwrap().0.clone(),
};
o
} }
/// ensures self is mutable, then returns a new instance of VData that is also mutable and uses the same Arc<Mutex<_>>. /// ensures self is mutable, then returns a new instance of VData that is also mutable and uses the same Arc<Mutex<_>>.
pub fn clone_mut(&mut self) -> Self { pub fn clone_mut(&mut self) -> Self {
// if not mutable, copy and set to mutable. // if not mutable, copy and set to mutable.
self.make_mut(); self.make_mut();
// now, both self and the returned value are set to mutable and share the same mutex. // now, both self and the returned value are set to mutable and share the same mutex.
self.clone_mut_assume() let o = self.clone_mut_assume();
o
} }
/// like clone_mut, but assumes self is already mutable, and therefor does not need to mutate self /// like clone_mut, but assumes self is already mutable, and therefor does not need to mutate self
/// as the Arc<Mutex<_>> will stay the same. /// as the Arc<Mutex<_>> will stay the same.
@ -61,9 +69,17 @@ impl VData {
/// makes self mutable. might clone. /// makes self mutable. might clone.
pub fn make_mut(&mut self) -> &mut Self { pub fn make_mut(&mut self) -> &mut Self {
{ {
let mut s = self.data.lock().unwrap(); let mut s = self.data();
if !s.1 { if !s.1 {
if Arc::strong_count(&self.data) > 1 {
// not mutable yet - clone the value to avoid modifying an immutable one.
#[cfg(debug_assertions)]
eprintln!("VData: actually copying value due to mutation of an immutable shared value. (strong count: {})", Arc::strong_count(&self.data));
*s = (s.0.clone(), true); *s = (s.0.clone(), true);
} else {
// value was immutable, but not shared, so we can just make it mutable.
s.1 = true;
}
} }
} }
self self
@ -74,14 +90,40 @@ impl VData {
} }
impl Clone for VData { impl Clone for VData {
fn clone(&self) -> Self { fn clone(&self) -> Self {
#[cfg(debug_assertions)]
eprint!("VData: Clone: ");
let mut d = self.data.lock().unwrap(); let mut d = self.data.lock().unwrap();
// set to immutable, locking the data as-is. let o = if d.1 {
if Arc::strong_count(&self.data) > 1 {
// mutable, copy the value to avoid accidentally modifying it.
#[cfg(debug_assertions)]
eprint!(
"copying value due to clone of a mutable shared value. (strong count: {})",
Arc::strong_count(&self.data)
);
d.0.clone().to()
} else {
// mutable, but not shared. just change it to not being mutable.
#[cfg(debug_assertions)]
eprint!("setting mutable value to immutable to avoid copying. clone will happen when value is used mutably.");
d.1 = false; d.1 = false;
// then return the same arc (-> avoid cloning) // then return the same arc (-> avoid cloning)
Self { Self {
data: Arc::clone(&self.data), data: Arc::clone(&self.data),
} }
} }
} else {
#[cfg(debug_assertions)]
eprint!("immutably cloning immutable value. no copy necessary.");
// immutable, return the same arc (-> avoid cloning)
Self {
data: Arc::clone(&self.data),
}
};
#[cfg(debug_assertions)]
eprintln!();
o
}
} }
impl Debug for VData { impl Debug for VData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {