Search code examples
rustwasm-bindgen

Making a rust wasm bindgen future example from cc::Build


I don't mean to ask too much, but I think I have surpassed the basic requirements using the Rust VS Code formatter. I'm positive futures are needed, and I believe the run_until local pool method can execute the operation. I think a lifetime elide is required, which I think exclusively is interpolated by the 'a Type declaration.

use futures::executor::LocalPool;

pub fn pathify(path: &str) -> std::path::PathBuf {
    let mut input_file = std::path::PathBuf::new();

    let arr: () = path.split("/").map(|x| input_file.push(x)).collect();
    return input_file;
}

struct DropOnce{  //ERROR: "expected named lifetime parameter"
    _bosun: dyn Mak<std::sync::Once, Output = cc::Build>, //::new()
}
trait Mak<'a,T:'a> { //fn resolve(self) -> cc::Build;
    type Output; //static types before compilation! (concrete type)
    fn _bosun() {
        let lock: std::path::PathBuf = pathify("./exec.c");
        cc::Build::new().file(lock).expand(); //= Default::default().await
    }
}
impl<'a> std::future::Future for DropOnce{
    fn poll(&'a mut self) -> &'a mut Self {
        println!("poll");
        self
    }
}
fn main() {
    let mut pool = LocalPool::new();
    let _bosun = DropOnce; //ERROR: "expected value, found struct `DropOnce`"
    pool.run_until(_bosun);
}

full code

use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen(start)]
fn main () {}

If this example is still too nascent to make work in a SO answer I would appreciate community documentation references. I think I may be confusing definitions in a trait and impl, like, '[are member functions of an impl as mutable as its trait definition+declaration, but for names]?'


Solution

  • Turns out, wasm-bindgen is not required for the cc::Build use case/the only use case for the latter.

    use serde::Serialize;
    use std::{
        collections::HashMap,
        future::Future,
        mem,
        pin::Pin,
        sync::mpsc::{channel, Sender},
        sync::{Arc, Mutex},
        task::{Context, Poll, Waker},
        thread::{self, JoinHandle},
        time::Duration,
    };
    #[derive(Serialize)]
    struct Product {
        ivity: String,
    }
    

    Evidently, the following is the minimum requirement of making a Future from an async{block.await} 'Task'.

    //(1) Reactor
    enum TaskState {
        Ready,
        NotReady(Waker),
        Finished,
    } //https://cfsamson.github.io/books-futures-explained/6_future_example.html
    struct Reactor {
        dispatcher: Sender<Event>,
        handle: Option<JoinHandle<()>>,
        tasks: HashMap<usize, TaskState>,
    }
    #[derive(Debug)]
    enum Event {
        Close,
        Timeout(u64, usize),
    }
    impl Reactor {
        fn new() -> Arc<Mutex<Box<Self>>> {
            let (tx, rx) = channel::<Event>();
            let reactor = Arc::new(Mutex::new(Box::new(Reactor {
                dispatcher: tx,
                handle: None,
                tasks: HashMap::new(),
            })));
            let reactor_clone = Arc::downgrade(&reactor);
            let handle = thread::spawn(move || {
                let mut handles = vec![];
                for event in rx {
                    println!("REACTOR: {:?}", event);
                    let reactor = reactor_clone.clone();
                    match event {
                        Event::Close => break,
                        Event::Timeout(duration, id) => {
                            let event_handle = thread::spawn(move || {
                                thread::sleep(Duration::from_secs(duration));
                                let reactor = reactor.upgrade().unwrap();
                                reactor.lock().map(|mut r| r.wake(id)).unwrap();
                            });
                            handles.push(event_handle);
                        }
                    }
                }
                handles
                    .into_iter()
                    .for_each(|handle| handle.join().unwrap());
            });
            reactor.lock().map(|mut r| r.handle = Some(handle)).unwrap();
            reactor
        }
        fn wake(&mut self, id: usize) {
            self.tasks
                .get_mut(&id)
                .map(|state| {
                    match mem::replace(state, TaskState::Ready) {
                        TaskState::NotReady(waker) => waker.wake(),
                        TaskState::Finished => {
                            panic!("Called 'wake' twice on task: {}", id)
                        }
                        _ => unreachable!(),
                    }
                })
                .unwrap();
        }
        fn register(&mut self, duration: u64, waker: Waker, id: usize) {
            if self.tasks.insert(id, TaskState::NotReady(waker)).is_some() {
                panic!("Tried to insert a task with id: '{}', twice!", id);
            }
            self.dispatcher.send(Event::Timeout(duration, id)).unwrap();
        }
        fn is_ready(&self, id: usize) -> bool {
            self.tasks
                .get(&id)
                .map(|state| match state {
                    TaskState::Ready => true,
                    _ => false,
                })
                .unwrap_or(false)
        }
    }
    
    impl Drop for Reactor {
        fn drop(&mut self) {
            self.dispatcher.send(Event::Close).unwrap();
            self.handle.take().map(|h| h.join().unwrap()).unwrap();
        }
    }
    //(2) Task
    #[derive(Clone)]
    pub struct Task {
        app: u64,
        reactor: Arc<Mutex<Box<Reactor>>>,
        id: usize,
    }
    impl Task {
        fn new(pathstr: &str, reactor: Arc<Mutex<Box<Reactor>>>, id: usize) -> Self {
            //Task {
            Task {
                app: match pathstr {
                    //let s: String = match pathstr {
                    "/" => {
                        fn pathify(path: &str) -> std::path::PathBuf {
                            let mut input_file = std::path::PathBuf::new();
                            let _arr: () =
                                path.split("/").map(|x| input_file.push(x)).collect();
                            return input_file;
                        }
                        let lock: std::path::PathBuf = pathify("./exec.c");
                        let appel = cc::Build::new().file(lock).expand();
                        //String::from_utf8(appel).unwrap()
                        u64::from_be_bytes(appel.try_into().expect(""))
                        //.iter().collect()
                    }
                    &_ => u64::from_be_bytes("".as_bytes().try_into().expect("")),
                    //};
                    //u64::from_str_radix(s.expect("")) //,16
                },
                reactor,
                id,
            }
        }
    }
    // (3) Future implementation
    impl Future for Task {
        type Output = usize;
    
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            //let mut r = self.app;
            let mut r = self.reactor.lock().unwrap();
            if r.is_ready(self.id) {
                *r.tasks.get_mut(&self.id).unwrap() = TaskState::Finished;
                Poll::Ready(self.id)
            } else if r.tasks.contains_key(&self.id) {
                r.tasks
                    .insert(self.id, TaskState::NotReady(cx.waker().clone()));
                Poll::Pending
            } else {
                r.register(self.app, cx.waker().clone(), self.id);
                Poll::Pending
            }
        }
    }
    let path = req.path(); //longer lived with let
    let pathstr: &str = path.as_str();
    let reactor = Reactor::new();
    let id = 1;
    let future1 = Task::new(pathstr, reactor.clone(), id);
    let fut1 = async {
        future1.await
        //let val = future1.await;
        //println!("Got {} at time: {:.2}.", val, start.elapsed().as_secs_f32());
    };
    Response::from_json(&Product {
        ivity: fut1.await.to_string(),
    })
    

    I'll edit this with use wasm_bindgen::prelude::*; use wasm_bindgen_futures::{JsFuture, future_to_promise}; as I build that out, yet I would not like to mislead with the question further with this passing rustup analyzer (a.k.a. I'm testing now, remember, VS code is obtuse like terminal which may require closing once).