Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async/Await semantics #7

Closed
rphmeier opened this issue Jan 12, 2016 · 3 comments
Closed

Async/Await semantics #7

rphmeier opened this issue Jan 12, 2016 · 3 comments

Comments

@rphmeier
Copy link
Owner

This would be definitely be a nice-to-have.

@rphmeier
Copy link
Owner Author

Something like this?

in lib.rs:

struct Dest<T> {
    flag: AtomicBool,
    dest: Option<T>,
}

impl<T> Dest<T> {
    // whether the destination has been filled.
    fn probe(&self) -> bool { self.counter.load(Relaxed) }

    // fill the destination with a value.
    fn fill(&mut self, val: T) {
        self.dest = Some(val);
        fence(SeqCst); // not sure what degree of synchronization is required here.
        self.counter.store(true, Relaxed);    
    }
}

pub struct Task<'a, T: 'a> {
    dest: Box<Dest<T>>,
    finished: bool,
    worker: &'a Worker,
}

impl<'a, T: 'a> Task<'a, T> {
    // helper function so we can call it in Drop impl.
    fn _await(&mut self) -> T {
        // busy wait until we're done.
        while !self.dest.counter.probe() {
             unsafe { self.worker.run_next() }
        }
        self.finished = true;
        self.dest.dest.take().unwrap();
    }

    // public interface consumes `self`, which is semantically correct.
    pub fn await(self) -> T {
        let mut s = self;
        s._await();
    }
}

impl<'a, T: 'a> Drop for Task<'a, T> {
    // we need to wait for the task to finish before we deallocate the boxed memory.
    // otherwise, the task will attempt to write into free'd memory.
    fn drop(&mut self) {
        if !self.finished {
            let _ = self._await();
        }
    }
}

impl<'pool, 'scope> Spawner<'pool, 'scope> {
    fn async<F, T>(&self, f: F) -> Task<T>
    where F: 'scope + Send + FnOnce() -> T, T: 'scope + Send {
        let mut task = Task {
            dest: Box::new(Dest {
                counter: AtomicBool::new(false),
                dest: None,
            });
            worker: self.worker
        };

        // wrapper type for sending raw pointers.
        let dest_ptr = SendPtr(&mut *task.dest as *mut _);
        self.submit(move || {
            unsafe { (*dest_ptr).fill(f()) }
        });
    }
}

This would probably work pretty well, but it may be unexpected that tasks are force-joined when dropped.

The worker busy-waiting pattern is becoming common enough that maybe a
Worker::wait_until<F: FnMut() -> bool>(&self, mut f: F) is warranted.

@rphmeier
Copy link
Owner Author

rphmeier commented Mar 3, 2016

The distinction between Spawner::submit and Spawner::async would likely be confusing to users.

@rphmeier
Copy link
Owner Author

not implementing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant