Promises, promises

This commit is contained in:
John Doty 2023-06-29 10:10:40 -07:00
parent 17fdee51e6
commit a2dafeea12
6 changed files with 303 additions and 102 deletions

View file

@ -1,11 +1,9 @@
use oden_js::{
module::loader::{ModuleLoader, ModuleSource},
Context, ContextRef, Promise, Result, Runtime, Value, ValueResult,
Context, ContextRef, Result, Runtime, Value,
};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::Path;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::mpsc::{channel, Receiver};
pub mod graphics;
@ -15,6 +13,7 @@ mod typescript;
use typescript::transpile_to_javascript;
pub mod assets;
pub mod io;
struct Loader {}
@ -39,21 +38,6 @@ impl ModuleLoader for Loader {
}
}
#[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)]
pub struct PromiseHandle(u64);
impl PromiseHandle {
pub fn new() -> Self {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
PromiseHandle(NEXT_ID.fetch_add(1, Ordering::SeqCst))
}
}
pub enum ScriptEvent {
AddPromise(PromiseHandle, Promise),
CompletePromise(PromiseHandle, Box<dyn FnOnce(&ContextRef) -> ValueResult>),
}
pub struct ScriptContext {
context: Context,
init: Value,
@ -63,9 +47,6 @@ pub struct ScriptContext {
gfx: graphics::GraphicsAPI,
_assets: assets::AssetsAPI,
gfx_receive: Receiver<graphics::GraphicsCommand>,
script_receive: Receiver<ScriptEvent>,
promises: HashMap<PromiseHandle, Promise>,
}
impl ScriptContext {
@ -78,12 +59,12 @@ impl ScriptContext {
context.add_intrinsic_operators();
let (gfx_send, gfx_receive) = channel();
let (script_send, script_receive) = channel();
let gfx = graphics::GraphicsAPI::define(&context, gfx_send.clone())
.expect("Graphics module should load without error");
let assets = assets::AssetsAPI::define(&context, gfx_send.clone())
.expect("Assets module should load without error");
let _io = io::IoAPI::define(&context).expect("IO module should load without error");
let module = context
.import_module("./src/main.ts", "")
@ -110,9 +91,6 @@ impl ScriptContext {
gfx_receive,
_assets: assets,
script_receive,
promises: HashMap::new(),
}
}
@ -127,31 +105,8 @@ impl ScriptContext {
}
pub fn update(&mut self) {
// Handle any promises that have completed before calling update.
while let Ok(event) = self.script_receive.try_recv() {
match event {
// TODO: Capture debugging information.
ScriptEvent::AddPromise(handle, promise) => {
self.promises.insert(handle, promise);
}
ScriptEvent::CompletePromise(handle, value_producer) => {
if let Some(promise) = self.promises.remove(&handle) {
let result = value_producer(&self.context);
match result {
Ok(v) => {
promise.resolve(&self.context, &v);
}
Err(e) => {
let error = e.to_js_error(&self.context);
promise.reject(&self.context, &error);
}
}
}
}
}
}
// Tell the runtime to process all pending "jobs".
// Tell the runtime to process all pending "jobs". This includes
// promise completions.
self.context
.process_all_jobs()
.expect("Error processing async jobs");

97
src/script/io.rs Normal file
View file

@ -0,0 +1,97 @@
use oden_js::{module::native::NativeModuleBuilder, ContextRef, ValueResult};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
type Job = Box<dyn FnOnce() + Send + 'static>;
struct ThreadPoolWorker {
_thread: thread::JoinHandle<()>,
}
impl ThreadPoolWorker {
fn new(queue: Arc<Mutex<Receiver<Job>>>) -> Self {
let thread = thread::spawn(move || loop {
let r = {
let locked = queue.lock();
locked.expect("Should not be orphaning the lock").recv()
};
if let Ok(item) = r {
item();
} else {
break;
}
});
ThreadPoolWorker { _thread: thread }
}
}
struct ThreadPool {
_workers: Vec<ThreadPoolWorker>,
sender: Sender<Job>,
}
impl ThreadPool {
fn new(size: usize) -> Self {
let (sender, receiver) = channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = vec![];
for _ in 0..size {
workers.push(ThreadPoolWorker::new(receiver.clone()));
}
ThreadPool {
_workers: workers,
sender,
}
}
fn execute(&self, job: Job) {
let _ = self.sender.send(job);
}
}
struct IoImpl {
thread_pool: ThreadPool,
}
impl IoImpl {
fn new() -> Self {
IoImpl {
thread_pool: ThreadPool::new(4),
}
}
fn load(&self, context: &ContextRef, path: &str) -> ValueResult {
let (value, promise) = context.new_promise()?;
let path = path.to_string();
self.thread_pool.execute(Box::new(move || {
// TODO: Actually read the path.
let path = path;
promise.resolve(move |ctx: &ContextRef| ctx.new_string(&path));
}));
Ok(value)
}
}
pub struct IoAPI {}
impl IoAPI {
pub fn define(ctx: &ContextRef) -> oden_js::Result<Self> {
let io = Arc::new(IoImpl::new());
let mut builder = NativeModuleBuilder::new(ctx);
{
let io = io.clone();
builder.export(
"load",
ctx.new_fn(move |ctx: &ContextRef, p: String| io.load(ctx, &p))?,
)?;
}
builder.build("io-core")?;
Ok(IoAPI {})
}
}