-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
qcell + selfref? #37
Comments
#30 basically explains what we have, but the idea would be to somehow end up with: struct MyCells<'id, 'this> {
x: LCell<'id, Foo>,
y: Cell<Option<&'this LCell<'id, dyn Bar>>>,
...
} or something similar (yeah the |
we're playing around with this idea, and it's not going well so far. :/ #![feature(generic_associated_types)]
use qcell::{LCell, LCellOwner};
use selfref::Holder;
use std::pin::Pin;
pub struct SRCellEnvironment<'k, T: selfref::Opaque + 'k> {
inner: Holder<'k, T>,
}
impl<'k, T: selfref::Opaque> SRCellEnvironment<'k, T> {
pub fn new_with<F: selfref::NewWith<'k, T>>(f: F) -> Self
where
T::Kind<'k>: Sized
{
Self {
inner: Holder::new_with(f),
}
}
}
impl<'k, T: selfref::Opaque> SRCellEnvironment<'k, T> {
pub fn operate_in<'i, F, R>(self: Pin<&'i mut Self>, f: F) -> R
where
F: OperateIn<'k, T, Out = R>
{
struct OperateInWrapper<F>(F);
impl<'k, T, F> selfref::OperateIn<'k, T> for OperateInWrapper<F>
where F: OperateIn<'k, T>, T: selfref::Opaque + 'k {
type Out = F::Out;
fn operate_in<'a>(self, x: Pin<&'a T::Kind<'a>>) -> Self::Out where 'k: 'a {
let OperateInWrapper(this) = self;
// SAFETY? we're actually unsure if this is sound!
let guard = unsafe {
generativity::Guard::new(generativity::Id::new())
};
this.operate_in(LCellOwnerWrapper(LCellOwner::new(guard)), x)
}
}
unsafe {
self.map_unchecked_mut(|this| &mut this.inner)
}.into_ref().operate_in(OperateInWrapper(f))
}
}
pub trait OperateIn<'k, T: selfref::Opaque + 'k> {
/// The value returned by this operation.
type Out;
/// Do this operation.
fn operate_in<'a>(self, owner: LCellOwnerWrapper<'a>, x: Pin<&'a T::Kind<'a>>) -> Self::Out where 'k: 'a;
}
pub struct LCellOwnerWrapper<'a>(LCellOwner<'a>);
impl<'a> Drop for LCellOwnerWrapper<'a> {
#[inline]
fn drop(&mut self) {
}
}
impl<'id> LCellOwnerWrapper<'id> {
pub fn cell<T>(&self, value: T) -> LCell<'id, T> {
self.0.cell(value)
}
pub fn ro<'a, T: ?Sized>(&'a self, lc: &'a LCell<'id, T>) -> &'a T {
self.0.ro(lc)
}
pub fn rw<'a, T: ?Sized>(&'a mut self, lc: &'a LCell<'id, T>) -> &'a mut T {
self.0.rw(lc)
}
}
#[test]
fn test() {
use std::cell::Cell;
use std::marker::PhantomData;
trait Foo {
fn update(&mut self);
}
trait Bar {
fn get(&self) -> String;
}
struct MyThing;
impl Foo for MyThing {
fn update(&mut self) {
println!("hello world!");
}
}
impl Bar for MyThing {
fn get(&self) -> String {
return String::from("hello");
}
}
struct IntrusiveLCell<'a, T: ?Sized> {
y: Cell<Option<&'a LCell<'a, dyn Bar>>>,
x: LCell<'a, T>,
}
struct IntrusiveLCellKey<T: ?Sized>(PhantomData<T>);
// SAFETY: T cannot refer to 'a so there's no unsoundness there. also the
// struct is a pretty basic self-ref struct with no Drop impl or glue which
// would otherwise make this unsound.
unsafe impl<T: ?Sized> selfref::Opaque for IntrusiveLCellKey<T> {
type Kind<'a> = IntrusiveLCell<'a, T>;
}
let mut holder = Box::pin(
SRCellEnvironment::<IntrusiveLCellKey<MyThing>>::new_with(
selfref::new_with_closure::<IntrusiveLCellKey<MyThing>, _>(|_| {
IntrusiveLCell {
x: LCell::new(MyThing),
y: Cell::new(None),
}
})
)
);
struct operate_in;
impl<'k> OperateIn<'k, IntrusiveLCellKey<MyThing>> for operate_in {
type Out = ();
fn operate_in<'a>(self, mut owner: LCellOwnerWrapper<'a>, x: Pin<&'a IntrusiveLCell<'a, MyThing>>) where 'k: 'a {
x.y.set(Some(&x.get_ref().x));
owner.rw(&x.x).update();
println!("{}", owner.ro(x.y.get().unwrap()).get());
}
}
holder.as_mut().operate_in(operate_in);
let mut holder_dyn: Pin<Box<SRCellEnvironment<IntrusiveLCellKey<dyn Foo>>>> = holder;
} (the |
I still think it would be better to deconstruct the fat pointer and store a pointer and several vtable pointers and reconstruct the fat pointer on request. I included some links in issue #30. Okay it is more low-level, and perhaps needs more thought to ensure soundness. But trying to do too much in the type system is just adding complexity. I still can't quite get my head around the |
it seems we hit rust-lang/rust#75899 aka rust-lang/rust#50213 so it's not our fault we ran into issues! but yeah we basically want something fully zero-cost! QCell is not zero-cost enough! LCell on the other hand is supposed to get optimized out. nevertheless, this is far more flexible than #30, and also far more flexible than "just going directly for the vtable pointers". tho it's certainly still suboptimal for just storing the vtable pointers, but aside from that it lets you have |
so this works with unsizing now huh. :p |
we've published a crate, no idea if it's sound, but feel free to play with it. https://users.rust-lang.org/t/soundness-review-request-srce-0-1/87587 |
we've rolled this up into |
would it be possible to have a cell type which is similar to
LCell
, but is based around theselfref
crate?(maybe with
generativity
? we're not familiar with that crate)this is related to #30 but more general
specifically we want a thread-safe lock-free
&mut SelfRefCellEnvironment
which opens a reusable LCell-like environment.The text was updated successfully, but these errors were encountered: