Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created December 12, 2021 22:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rust-play/8106a2e9d323bab7fd5884b124ce201e to your computer and use it in GitHub Desktop.
Save rust-play/8106a2e9d323bab7fd5884b124ce201e to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
////////////////////////////////////////////////////////////////////////////////
// This section from https://docs.rs/existential/0.1.1/src/existential/lib.rs.html#106-113
pub trait TyConFor<'a> {
type Applied;
}
pub type Apply<'a, C> = <C as TyConFor<'a>>::Applied;
pub trait TyCon: for<'a> TyConFor<'a> {}
impl<C: ?Sized> TyCon for C where C: for<'a> TyConFor<'a> {}
////////////////////////////////////////////////////////////////////////////////
pub mod scoped_existential {
use super::*;
use std::mem;
use std::sync::{Arc, RwLock, TryLockError, Weak};
pub fn scope<'scope, F, R>(f: F) -> R
where
F: FnOnce(&mut Scope<'scope>) -> R,
{
f(&mut Scope {
data: <_>::default(),
lock: <_>::default(),
})
}
pub struct Scope<'scope> {
data: Vec<Arc<dyn Send + Sync + 'scope>>,
lock: Arc<RwLock<()>>,
}
impl<C: TyCon> Clone for ScopedExistential<C> {
fn clone(&self) -> Self {
Self {
payload: self.payload.clone(),
lock: self.lock.clone(),
}
}
}
// TODO: Send/Sync implementation is slightly inaccurate (due to 'static)
pub struct ScopedExistential<C: TyCon> {
payload: Weak<Apply<'static, C>>,
lock: Arc<RwLock<()>>,
}
impl<C: TyCon> ScopedExistential<C> {
pub fn with_upgrade<F, R>(&self, f: F) -> R
where
F: FnOnce(Option<&Apply<'_, C>>) -> R,
{
match self.lock.try_read() {
Err(TryLockError::Poisoned(_)) => unreachable!(),
Err(TryLockError::WouldBlock) => f(None),
Ok(guard) => match self.payload.upgrade() {
None => {
drop(guard);
f(None)
}
Some(payload) => {
let r = f(Some(&payload));
drop(payload);
drop(guard);
r
}
},
}
}
}
impl<'scope> Scope<'scope> {
/// erases lifetime "'a" from a type Arc<Foo<'a>>
pub fn erase<'a: 'scope, C: TyCon>(&mut self, val: Apply<'a, C>) -> ScopedExistential<C>
where
C: 'scope,
Apply<'a, C>: Send + Sync,
{
let val = Arc::new(val);
let weak = Arc::downgrade(&val);
self.data.push(val);
let payload =
unsafe { mem::transmute::<Weak<Apply<'a, C>>, Weak<Apply<'static, C>>>(weak) };
ScopedExistential {
payload,
lock: self.lock.clone(),
}
}
}
impl<'scope> Drop for Scope<'scope> {
fn drop(&mut self) {
let guard = self.lock.write().unwrap();
self.data.clear();
drop(guard);
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Useage without further unsafe code...
use scoped_existential::{scope, Scope};
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
// assume scoped_existential was a different crate, so we use an extension trait
trait ExtendMut<'scope> {
fn extend_mut<'long, A, R, E, F>(
&mut self,
error: E,
func: F,
) -> Box<dyn 'long + FnMut(A) -> R + Send>
where
'long: 'scope,
A: 'long,
R: 'long,
E: 'long + FnMut(A) -> R + Send,
F: 'scope + FnMut(A) -> R + Send;
}
impl<'scope> ExtendMut<'scope> for Scope<'scope> {
fn extend_mut<'long, A, R, E, F>(
&mut self,
mut error: E,
func: F,
) -> Box<dyn 'long + FnMut(A) -> R + Send>
where
'long: 'scope,
A: 'long,
R: 'long,
E: 'long + FnMut(A) -> R + Send,
F: 'scope + FnMut(A) -> R + Send,
{
let func = Mutex::new(Box::new(func) as _);
struct BoxDynFnMutSend<A, R>(PhantomData<fn() -> (A, R)>);
impl<'a, A, R> TyConFor<'a> for BoxDynFnMutSend<A, R> {
type Applied = Mutex<Box<dyn 'a + FnMut(A) -> R + Send>>;
}
let func = self.erase::<BoxDynFnMutSend<A, R>>(func);
Box::new(move |a| {
func.with_upgrade(|maybe_func| match maybe_func {
None => error(a),
Some(func) => func.try_lock().unwrap()(a),
})
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
#[test]
fn test_in_scope() {
let errorstate: Arc<Mutex<i32>> = Arc::new(Mutex::new(0));
let clone = errorstate.clone();
let error = move |()| {
*clone.lock().unwrap() += 3;
};
let mut i: Box<i32> = Box::new(0);
scope(|s| {
let mut extended = s.extend_mut(error, |()| {
*i += 2;
});
extended(());
});
assert_eq!(*i, 2);
assert_eq!(*errorstate.lock().unwrap(), 0);
}
#[test]
fn test_out_of_scope() {
let errorstate: Arc<Mutex<i32>> = Arc::new(Mutex::new(0));
let clone = errorstate.clone();
let error = move |()| {
*clone.lock().unwrap() += 7;
};
let mut i: Box<i32> = Box::new(0);
let mut extended = scope(|s| {
let extended = s.extend_mut(error, |()| {
*i += 5;
});
extended
});
extended(());
drop(extended);
assert_eq!(*i, 0);
assert_eq!(*errorstate.lock().unwrap(), 7);
}
#[test]
fn test_out_of_scope_with_drop() {
let errorstate: Arc<Mutex<i32>> = Arc::new(Mutex::new(0));
let clone = errorstate.clone();
let error = move |()| {
*clone.lock().unwrap() += 13;
};
let mut i: Box<i32> = Box::new(0);
let mut extended = scope(|s| {
let extended = s.extend_mut(error, |()| {
*i += 11;
});
extended
});
assert_eq!(*i, 0);
drop(i);
extended(());
assert_eq!(*errorstate.lock().unwrap(), 13);
}
#[test]
fn test_automatic_dropping() {
let dropcheck: Arc<Mutex<i32>> = Arc::new(Mutex::new(0));
let extended = {
let extended = scope(|s| {
struct MonitorDrop(Arc<Mutex<i32>>);
impl Drop for MonitorDrop {
fn drop(&mut self) {
*self.0.lock().unwrap() += 1;
}
}
let r = MonitorDrop(dropcheck.clone());
let f = move |()| {
let _ = &r;
};
let extended = s.extend_mut(|()| (), f);
assert_eq!(*dropcheck.lock().unwrap(), 0);
extended
});
assert_eq!(*dropcheck.lock().unwrap(), 1);
extended
};
drop(extended);
}
}
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn _main() {
let string = "Hello World".to_owned();
let (tx1, rx1) = mpsc::channel::<()>();
let (tx2, rx2) = mpsc::channel::<()>();
let handle = scope(|s| {
let string_ref = string.as_str();
let mut f = s.extend_mut(
|()| (),
move |()| {
tx1.send(()).unwrap();
// use timeout to avoid deadlock in case `scope`
// was correctly implemented
let _ = rx2.recv_timeout(Duration::from_secs(1)); // ignore timeout
println!("{}", string_ref);
},
);
let handle = thread::spawn(move || {
f(());
});
rx1.recv().unwrap();
handle
});
drop(string);
println!("DROPPED");
let _ = tx2.send(()); // ignore disconnected
handle.join().unwrap();
}
use std::mem;
fn main() {
scope(|s| {
replace_with::replace_with_or_abort(s, |s| {
let string = "Hello World".to_owned();
let string_ref = &string;
let mut s = s; // variance!
let mut f = s.extend_mut(
|()| (),
|()| {
println!("{}", string);
},
);
mem::forget(s);
drop(string);
f(()); // accesses the string after free
panic!(); // results in abort
})
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment