Ownership is theft? Tockã®TakeCellã«ã¤ãã¦
tockã¨ããrust製ã®çµã¿è¾¼ã¿åãOSãããã¾ãï¼ ãã®tockã®ä½æè ãã2015å¹´ã«Ownership is Theft: Experiences Building an Embedded OS in Rust (PLOS 2015)ã¨ããè«æãçºè¡¨ãã¾ããï¼ããã§ã¯èè ãtockãéçºããä¸ã§åµã£ãownershipã«é¢ããåé¡ã¨ï¼ããã解決ããããã®è¨èªã®ä¿®æ£ã¢ããã¼ããè¿°ã¹ããã¦ãã¾ãï¼ãã ãã®å¾rustã®éçºè ã¨ã®ããã¨ããããï¼è¨èªã«ä¿®æ£ãå ãããã¨ãªãå½åå®è£ ãããã£ããã¨ãå®è£ ã§ããããã§ãï¼The Case for Writing a Kernel in Rust (APSys'17)ã«ãã®ãã¨ãç°¡åã«æ¸ããã¦ãã¾ãï¼
Ownershipã¨åé¡
ãåç¥ã®éãrustã«ã¯ã¡ã¢ãªå®å ¨ãä¿è¨¼ããããã«ownershipã¨ããä»çµã¿ãããï¼ãã¨ãã·ã³ã°ã«ã¹ã¬ããã§ãåä¸ãã¼ã¿ã«å¯¾ããmutableãªreferenceãè¤æ°æã¤ãã¨ãã§ãã¾ããï¼ã¨ãã£ã¦ãmutableãªreferenceãè¤æ°æ¬²ãå ´åã¯å¾ã ã«ãã¦ããã¾ãï¼è«æã§ã¯ä»¥ä¸ã®ãããªæ§æã®ä¹±æ°çæå¨å¼ã³åºããä¾ã«æãã¦ãã¾ãï¼
SysCallDispatcher <--> SimpleRNG <---> RNG
pub struct SimpleRNG { pub busy: bool, } impl SimpleRNG { pub fn command(&mut self) { self.busy = true; //... } pub fn deliver(&mut self, rand: u32) { self.busy = false; //... } } pub struct SysCallDispatcher<'a> { pub srng: &'a mut SimpleRNG, } pub struct RNG<'a> { pub srng: &'a mut SimpleRNG, } impl<'a> SysCallDispatcher<'a> { pub fn dispatch(&mut self) { self.srng.command(); } } impl<'a> RNG<'a> { pub fn done(&mut self, num: u32) { self.srng.deliver(num); } }
ããã§ã¯SysCallDispatcher
ã¨RNG
ãã¨ãã«SimpleRNG
ã®referenceãææããæ§æãèãã¦ãã¾ããï¼ãã®ã³ã¼ããå®éã«ä½¿ç¨ããå ´åã¯borrow checkã«å¼ã£ãããã³ã³ãã¤ã«ã§ãã¾ããï¼
let mut srng = SimpleRNG { busy: false }; let mut dispathcer = SysCallDispatcher { srng: &mut srng }; // ã¨ã©ã¼: cannot borrow `srng` as mutable more than once at a time let mut rng = RNG { srng: &mut srng };
解決ç1. Cell
ãã®ãããªå ´åã®è§£æ±ºçã®ä¸ã¤ã¯Cellã使ããã¨ã§ãï¼
use std::cell::Cell; pub struct SimpleRNG { pub busy: Cell<bool>, } impl SimpleRNG { pub fn command(&self) { self.busy.set(true); } pub fn deliver(&self, rand: u32) { self.busy.set(false); } } pub struct SysCallDispatcher<'a> { pub srng: &'a SimpleRNG, } pub struct RNG<'a> { pub srng: &'a SimpleRNG, } impl<'a> SysCallDispatcher<'a> { pub fn dispatch(&self) { self.srng.command(); } } impl<'a> RNG<'a> { pub fn done(&self, num: u32) { self.srng.deliver(num); } }
ããããã¨ã³ã³ãã¤ã«ãéãããã«ãªãã¾ãï¼
let mut srng = SimpleRNG { busy: false }; let mut dispathcer = SysCallDispatcher { srng: &srng }; let mut rng = RNG { srng: &srng };
ãã¤ã³ãã¨ãã¦ã¯ä»åSysCallDispatcher
ãRNG
ã¯&mut T
ã§ã¯ãªã&T
ãä¿æãã¦ããç¹ã§ãï¼imutableãªreferenceãªã®ã§borrow checkerã«ã²ãããã¾ããï¼Cellã¯å
é¨çã«unsafeãªã³ã¼ããå©ç¨ãããã¨ã§å¤ãå¤æ´ãã¾ãï¼get()
ã¯å¤ã®ã³ãã¼ãè¿ãï¼set()
ã§ã¯std::mem::replace()
ãå©ç¨ãã¦å¤ãæ¸ãæãã¾ãï¼
Cellã¯Copy
ãå®è£
ãã¦ããåãã使ãã¾ããï¼ã¾ãCellã¯Syncãå®è£
ãã¦ããªãããï¼Cellãã¹ã¬ããéã§å
±æãããããªã³ã¼ãã¯ã³ã³ãã¤ã«ã¨ã©ã¼ã«ãªãã¾ãï¼ãã®ãããã¼ã¿ç«¶åãçãããã¨ã¯ããã¾ããï¼
Copy
ãå®è£
ãã¦ããåãã使ããªãã¨ããã®ã¯å¤§ããªå¶ç´ã§ãï¼ããªããã£ãåã¯Cellã§ããã§ããï¼ãããã¡é åãªã©ãå
±æãããã¨ãã§ãã¾ãã(&mut T
ã¯Copy
ãå®è£
ãã¦ãã¾ãã)ï¼
解決ç2. TakeCell
ãããã¡é åãªã©ãå ±æããããã«ï¼tockã§ã¯TakeCellã¨ãããã®ãå©ç¨ãã¦ãã¾ãï¼TakeCellã¯Cellã¨ä¼¼ã¦ãã¾ããï¼ä»¥ä¸ã®ãã¼ã¿æ§é ãå©ç¨ãã¦referenceãä¿æãã¾ãï¼
pub struct TakeCell<'a, T: 'a + ?Sized> { val: UnsafeCell<Option<&'a mut T>>, }
take()
ãå©ç¨ãã¦TakeCellã®å¤ãåå¾ãããã¨ãã§ãã¾ãï¼ããä»®ã«TakeCellã®ä¸èº«ãå¥ããåå¾ããã¦ããå ´åã¯None
ãè¿ãã¾ãï¼
pub fn take(&self) -> Option<&'a mut T> { unsafe { let inner = &mut *self.val.get(); inner.take() } }
ã¾ãï¼ã¯ãã¼ã¸ã£ããTakeCellã®ãã¼ã¿ãç°¡åã«å©ç¨ã§ããããã«map()
ã¨ããã¡ã½ãããæä¾ãã¦ãã¾ãï¼
pub fn map<F, R>(&self, closure: F) -> Option<R> where F: FnOnce(&mut T) -> R { let maybe_val = self.take(); maybe_val.map(|mut val| { let res = closure(&mut val); self.replace(val); res }) }
take()
ããã¨TakeCellã®ãã¼ã¿ã¯None
ã«ãªãã¾ãï¼ããã§ãã¼ã¿ãTakeCellã«æ»ãããå ´åã¯map()
ã®ã³ã¼ãã«ããããã«æ示çã«æ»ãå¿
è¦ãããã¾ãï¼
TakeCellã使ãã¨ä¾ãã°ä»¥ä¸ã®ãããªã³ã¼ããæ¸ãã¾ãï¼
pub struct DMAChannel { pub buffer: TakeCell<'static, [u8]>, } impl DMAChannel { pub fn foo(&mut self) { self.buffer.map(|b| { //... }); } } static mut buffer: [u8; 128] = [0; 128]; fn foo(){ let mut chan = unsafe { DMAChannel { buffer: TakeCell::new(&mut buffer), } }; chan.foo(); }
staticãªborrowãä½ãããã«ã¯unsafeãªã³ã¼ããå¿ è¦ã§ãï¼
RefCellã¨try_borrow_mut
rustãæ¸ãããã¨ããã人ãªãTakeCellãªããå©ç¨ããªãã¦ãRefCellã§ããã®ã§ã¯?ã¨æãã¨æãã¾ãï¼
RefCellã¯get()
, set()
ã®ä»£ããã«borrow()
åã³borrow_mut()
ãæä¾ãï¼å¤ã®referenceãæä½ãããã¨ãã§ãã¾ãï¼RefCellã¯Copy
ãå®è£
ãã¦ããªãã¦ã使ç¨ãããã¨ãã§ãã¾ãï¼
RefCellæ大ã®ç¹å¾´ã¯ã³ã³ãã¤ã«æã§ã¯ãªãå®è¡æã«borrow checkãããã¨ãããã¨ã§ãï¼ããä»®ã«è¤æ°ã®mutableãªreferenceã使ç¨ã¨ããå ´åï¼å®è¡æã«panicãã¾ãï¼RefCellã使ã£ã¦å®å
¨ãªã³ã¼ããæ¸ãã®ã¯ããã°ã©ãã®è²¬ä»»ã§ãï¼ã«ã¼ãã«å
ã§panicãã¦ãã¾ãã®ã¯å¤§å¤æã¾ããããã¾ããï¼TakeCellã®å ´åã¯ããtake()
ããã¦ãããNoneãè¿ãã¾ã (map()
ã®å ´åã¯ä½ãããªãã§çµããã¾ã). ãã ãï¼RefCellã«ã¯try_borrow_mut()
ã¨ããã¡ã½ãããããï¼ããä»®ã«ãã§ã«mutableãªrreferenceãåå¾ããã¦ããå ´åErr
ãè¿ãã¾ãï¼
TakeCellãå©ç¨ããªãã¦ãï¼try_borrow_mut()
ã§ããã¨åçãªæ©è½ãã§ãããããªæ°ããã¾ãï¼
ããã§ã¯ãªãtockãTakeCellã使ã£ã¦ããã®ãã¨ããã¨... ãã®redditã®ã³ã¡ã³ãã«ãã㨠TakeCellãã§ããã®ã¯try_borrow_mut()
ãå°å
¥ããããããå *1 ã¨ããã®ãæ大ã®çç±ã®ããã§ãï¼
ã¡ãªã¿ã«ï¼tockã«ã¯TakeCellã¨ãããä¼¼ã¦ããMapCellã¨ãããã¼ã¿æ§é ãããã¾ãï¼MapCellã¯referenceã§ã¯ãªãå®éã®å¤ãä¿æããããã«ãªã£ã¦ããï¼å®éã«å¤ãä¿æãã¦ãããã¯occupied
ã¨å¼ã°ãããã£ã¼ã«ãã§ä¿æãã¦ãã¾ãï¼ä¸æ¹TakeCell
ã¯å¤ãreferenceã«éå®ãããã¨ã§Optionã®non-null optimizationãå©ç¨ãã¦occupied
åã®ãã¼ã¿æ§é ãç¯ç´ãã¦ãã¾ã*2ï¼
ã¾ãï¼æ®éã ã¨Cell
ãRefCell
ã¯Rc
ã¨çµã¿åããã¦å©ç¨ãããã¨ãå¤ãã¨æãã¾ããä»åã«ã¼ãã«å
ã§åçã«ã¡ã¢ãªã確ä¿ãããã¨ã¯èãã¦ããªãã®ã§Rc
ã®å©ç¨ã¯ãªãã·ã§ã³å¤ã§ãï¼tockã ã¨åºæ¬çã«TakeCell
ã§ã¯'static
ãªãããã¡é åãå
±æããããã§ãï¼