Skip to content

Instantly share code, notes, and snippets.

Created June 19, 2017 06:34
Show Gist options
  • Save anonymous/042f5912b0f933ed66ed8868367f84ed to your computer and use it in GitHub Desktop.
Save anonymous/042f5912b0f933ed66ed8868367f84ed to your computer and use it in GitHub Desktop.
Shared via Rust Playground
use std::cell::UnsafeCell;
use std::ops::*;
use std::marker::PhantomData;
pub struct DefaultTag {}
pub struct SyncCell<'cc, T> {
unsafe_cell: UnsafeCell<T>,
phantom_borrow_creator: PhantomData<&'cc ()>,
}
unsafe impl<'cc, T> Sync for SyncCell<'cc, T> where T : Sync {}
impl<'cc, T> SyncCell<'cc, T> {
// Open the cell immutably. Requires an immutable borrow of the key.
#[inline(always)]
pub fn open<'a>(&'a self, _key: &'a SyncCellKey) -> &'a T {
unsafe { &*self.unsafe_cell.get() }
}
// Open the cell mutably. Requires a mutable borrow of the key.
#[inline(always)]
pub fn open_mut<'a>(&'a self, _key: &'a mut SyncCellKey) -> &'a mut T {
unsafe { &mut *self.unsafe_cell.get() }
}
// Open the cell without a key. Requires a mutable borrow of the cell.
//
// I'd like to impl DerefMut to do this, but I can't,
// because that requires me to provide Deref also, which is impossible.
#[inline(always)]
fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.unsafe_cell.get() }
}
}
pub struct SyncCellKey {
_non_pub_member: (),
}
// This has a similar signature to std::thread::spawn(),
// so when you call this, you cannot assume
pub fn create_synccell_env<F, R>(f: F) -> R
where
F: Send + 'static,
F: for<'key, 'cc> FnOnce(&'key mut SyncCellKey, &'cc SyncCellCreator) -> R,
{
let mut key = SyncCellKey { _non_pub_member: () };
let cell_creator = SyncCellCreator { _non_pub_member: () };
f(&mut key, &cell_creator)
}
pub struct SyncCellCreator {
_non_pub_member: (),
}
impl SyncCellCreator {
pub fn create_cell<'cc, T>(&'cc self, value: T) -> SyncCell<'cc, T> {
SyncCell {
unsafe_cell: UnsafeCell::new(value),
phantom_borrow_creator: PhantomData,
}
}
}
fn main() {
create_synccell_env(move |sync_key, cell_creator| {
// creating cells is done without the key
let mut cell_a = cell_creator.create_cell(3i32);
let mut cell_b = cell_creator.create_cell(7i32);
// can access multiple cells immutably at the same time
{
let _a = cell_a.open(sync_key);
let _b = cell_b.open(sync_key);
}
// can access one cell at a time mutably
{
cell_a.open_mut(sync_key);
cell_b.open_mut(sync_key);
}
// can't access cell b while mutably accessing cell a
{
let _a = cell_a.open_mut(sync_key);
//let _b = cell_b.open(sync_key); // error
}
// you CAN access both cells mutably at the same time
// without even using the accessor,
// if you have a &mut to both of them
{
let a: &mut i32 = cell_a.get_mut();
let b: &mut i32 = cell_b.get_mut();
assert_eq!(*a, 3);
assert_eq!(*b, 7);
}
// to use SeqCells in functions, pass the accessor around by (mut) reference
{
increment(&cell_a, sync_key);
assert_eq!(*cell_a.open(sync_key), 4);
}
// You can create multiple keys and creators, even in a nested way
let _ = create_synccell_env(move |_sync_key2, _cell_creator2| {
// but you can't access keys, cell_creators or cells from a different environment
// (at least you shouldn't be able to or this whole thing is unsound)
//sync_key; // error
//cell_creator; // error
//cell_a; // error
// trying to escape the environment through the return value
// should also be impossible
//_sync_key2 // error
//_cell_creator2 // error
//_cell_creator2.create_cell(3i32) // error
});
});
}
// Note that this is not actually zero-overhead,
// because even though SyncCellKey is zero-sized, the reference to it is not.
//
// This pre-RFC would solve that:
// https://users.rust-lang.org/t/pre-rfc-zero-sized-references-to-zero-sized-types/7744
//
// Alternatively, the SyncCellKey can be passed around by value,
// but that leads to very ugly code.
//
// Either way, the impact on performance should be pretty small with sufficient inlining.
fn increment(cell: &SyncCell<i32>, sync_key: &mut SyncCellKey) {
*cell.open_mut(sync_key) += 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment