Created
June 19, 2017 06:34
-
-
Save anonymous/042f5912b0f933ed66ed8868367f84ed to your computer and use it in GitHub Desktop.
Shared via Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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