Created
February 28, 2018 09:21
-
-
Save shadowmint/7417c6cd531817b24f218da972c42123 to your computer and use it in GitHub Desktop.
Rust observable
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
extern crate core; | |
use std::sync::atomic::AtomicBool; | |
use std::mem::transmute; | |
use std::sync::atomic::Ordering; | |
pub struct Observer { | |
hidden: Option<*const HiddenState> | |
} | |
pub struct Observable { | |
hidden: Option<*const HiddenState> | |
} | |
struct HiddenState { | |
observer: AtomicBool, | |
observable: AtomicBool, | |
} | |
impl Observable { | |
pub fn new() -> (Observer, Observable) { | |
let state = Box::new(HiddenState { | |
observable: AtomicBool::new(true), | |
observer: AtomicBool::new(true), | |
}); | |
let raw_state: *const HiddenState; | |
unsafe { | |
// This pointer is now an indefinitely valid static reference. | |
raw_state = transmute(state); | |
} | |
let observer = Observer { hidden: Some(raw_state) }; | |
let observable = Observable { hidden: Some(raw_state) }; | |
return (observer, observable); | |
} | |
// This is safe because no other mutable references can exist of HiddenState::observable | |
fn atomic_state(&mut self) -> &mut AtomicBool { | |
unsafe { | |
let state_ref = self.immutable_state(); | |
let atomic_state_ref = &state_ref.observable as *const AtomicBool; | |
return &mut (*(atomic_state_ref as *mut AtomicBool)); | |
} | |
} | |
// This is safe because it returns an immutable reference | |
fn immutable_state(&mut self) -> &HiddenState { | |
unsafe { | |
let state_ref = &(**(self.hidden.as_ref().unwrap())); | |
return state_ref; | |
} | |
} | |
} | |
impl Observer { | |
// This is safe because no other mutable references can exist of HiddenState::observer | |
fn atomic_state(&mut self) -> &mut AtomicBool { | |
unsafe { | |
let state_ref = self.immutable_state(); | |
let atomic_state_ref = &state_ref.observer as *const AtomicBool; | |
return &mut (*(atomic_state_ref as *mut AtomicBool)); | |
} | |
} | |
// This is safe because it returns an immutable reference | |
fn immutable_state(&mut self) -> &HiddenState { | |
unsafe { | |
let state_ref = &(**(self.hidden.as_ref().unwrap())); | |
return state_ref; | |
} | |
} | |
} | |
impl HiddenState { | |
fn should_drop(&self) -> bool { | |
return !self.observer.fetch_or(self.observable.load(Ordering::Acquire), Ordering::Acquire); | |
} | |
} | |
impl Drop for Observable { | |
fn drop(&mut self) { | |
self.atomic_state().store(false, Ordering::Acquire); | |
if self.immutable_state().should_drop() { | |
unsafe { | |
transmute::<*const HiddenState, Box<HiddenState>>(self.hidden.take().unwrap()); | |
} | |
} | |
} | |
} | |
impl Drop for Observer { | |
fn drop(&mut self) { | |
self.atomic_state().store(false, Ordering::Acquire); | |
if self.immutable_state().should_drop() { | |
unsafe { | |
transmute::<*const HiddenState, Box<HiddenState>>(self.hidden.take().unwrap()); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment