From 231bc3cc462e6dc416f0fb13588946dd3949a3bf Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 7 Sep 2016 02:58:37 +0000 Subject: [PATCH] Yielder: use Cell internally to allow suspending via multiple borrows. The use case is as follows: let's say I have a scheduler that uses libfringe for context switching. A single thread may open several sockets, each of which presents a std::io::Read interface, and waiting in Read::read requires having a reference to the yielder inside the socket. This could be worked around with RefCell in the API consumer, but there is no real reason to not implement it in libfringe. close #48 --- src/generator.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 225ffa5..79fe2d1 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -13,6 +13,7 @@ use core::marker::PhantomData; use core::{ptr, mem}; +use core::cell::Cell; use stack; use context::Context; @@ -109,10 +110,10 @@ impl Generator F: FnOnce(&mut Yielder, Input) { // Retrieve our environment from the callee and return control to it. let (mut yielder, f) = ptr::read(env as *mut (Yielder, F)); - let data = Context::swap(yielder.context, yielder.context, 0); + let data = Context::swap(yielder.context.get(), yielder.context.get(), 0); // See the second half of Yielder::suspend_bare. let (new_context, input) = ptr::read(data as *mut (*mut Context, Input)); - yielder.context = new_context as *mut Context; + yielder.context.set(new_context as *mut Context); // Run the body of the generator. f(&mut yielder, input); // Past this point, the generator has dropped everything it has held. @@ -185,7 +186,7 @@ impl Generator /// returns a value. #[derive(Debug)] pub struct Yielder { - context: *mut Context, + context: Cell<*mut Context>, phantom: (PhantomData<*const Input>, PhantomData<*const Output>) } @@ -193,22 +194,22 @@ impl Yielder where Input: Send, Output: Send, Stack: stack::Stack { fn new(context: *mut Context) -> Yielder { Yielder { - context: context, + context: Cell::new(context), phantom: (PhantomData, PhantomData) } } #[inline(always)] - fn suspend_bare(&mut self, mut val: Option) -> Input { + fn suspend_bare(&self, mut val: Option) -> Input { unsafe { - let data = Context::swap(self.context, self.context, + let data = Context::swap(self.context.get(), self.context.get(), &mut val as *mut Option as usize); mem::forget(val); let (new_context, input) = ptr::read(data as *mut (*mut Context, Input)); // The generator can be moved (and with it, the context). // This changes the address of the context. // Thus, we update it after each swap. - self.context = new_context; + self.context.set(new_context); // However, between this point and the next time we enter suspend_bare // the generator cannot be moved, as a &mut Generator is necessary // to resume the generator function. @@ -219,7 +220,7 @@ impl Yielder /// Suspends the generator and returns `Some(item)` from the `resume()` /// invocation that resumed the generator. #[inline(always)] - pub fn suspend(&mut self, item: Output) -> Input { + pub fn suspend(&self, item: Output) -> Input { self.suspend_bare(Some(item)) } }