Skip to content
This repository was archived by the owner on Jun 10, 2024. It is now read-only.

Commit 1086514

Browse files
authored
Merge pull request #206 from lumen/non-blocking
Non-blocking web scheduler loop
2 parents 1d7f0ac + c9ea09a commit 1086514

File tree

10 files changed

+396
-360
lines changed

10 files changed

+396
-360
lines changed

Cargo.lock

Lines changed: 97 additions & 53 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/spawn-chain/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ time_web_sys = ["parking_lot_core/time_web_sys", "lumen_runtime/time_web_sys"]
1818
# code size when deploying.
1919
console_error_panic_hook = { version = "0.1.1", optional = true }
2020

21+
js-sys = "0.3.25"
2122
liblumen_alloc = { path = "../../liblumen_alloc" }
2223
lumen_runtime = { path = "../../lumen_runtime" }
2324
lumen_web = { path = "../../lumen_web" }
@@ -42,4 +43,6 @@ features = ['console']
4243

4344
[dev-dependencies]
4445
time-test = "0.2.1"
46+
futures = "0.1.28"
47+
wasm-bindgen-futures = "0.3.26"
4548
wasm-bindgen-test = "0.2"

examples/spawn-chain/src/lib.rs

Lines changed: 20 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,17 @@ mod apply_3;
77
mod elixir;
88
mod start;
99

10-
use std::convert::TryInto;
11-
12-
use liblumen_alloc::erts::exception;
1310
use liblumen_alloc::erts::process::code::stack::frame::Placement;
14-
use liblumen_alloc::erts::process::{heap, next_heap_size, Status};
11+
use liblumen_alloc::erts::process::{heap, next_heap_size};
1512

1613
use lumen_runtime::scheduler::Scheduler;
17-
use lumen_runtime::system;
1814

1915
use lumen_web::wait;
2016

2117
use wasm_bindgen::prelude::*;
2218

2319
use crate::elixir::chain::{console_1, dom_1};
2420
use crate::start::*;
25-
use liblumen_alloc::erts::term::atom_unchecked;
2621

2722
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
2823
// allocator.
@@ -34,15 +29,16 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
3429
pub fn start() {
3530
set_panic_hook();
3631
set_apply_fn();
32+
lumen_web::start();
3733
}
3834

3935
#[wasm_bindgen]
40-
pub fn log_to_console(count: usize) -> usize {
36+
pub fn log_to_console(count: usize) -> js_sys::Promise {
4137
run(count, Output::Console)
4238
}
4339

4440
#[wasm_bindgen]
45-
pub fn log_to_dom(count: usize) -> usize {
41+
pub fn log_to_dom(count: usize) -> js_sys::Promise {
4642
run(count, Output::Dom)
4743
}
4844

@@ -51,160 +47,34 @@ enum Output {
5147
Dom,
5248
}
5349

54-
fn run(count: usize, output: Output) -> usize {
50+
fn run(count: usize, output: Output) -> js_sys::Promise {
5551
let arc_scheduler = Scheduler::current();
5652
// Don't register, so that tests can run concurrently
5753
let parent_arc_process = arc_scheduler.spawn_init(0).unwrap();
5854

59-
// if not enough memory here, resize `spawn_init` heap
60-
let count_term = parent_arc_process.integer(count).unwrap();
61-
6255
let heap_size = next_heap_size(79 + count * 5);
6356
// if this fails the entire tab is out-of-memory
6457
let heap = heap(heap_size).unwrap();
6558

66-
let run_arc_process = wait::with_return_0::spawn(
59+
wait::with_return_0::spawn(
6760
&parent_arc_process,
6861
heap,
69-
heap_size
70-
)
71-
// if this fails use a bigger sized heap
72-
.unwrap();
73-
74-
match output {
75-
Output::Console => {
76-
// if this fails use a bigger sized heap
77-
console_1::place_frame_with_arguments(&run_arc_process, Placement::Push, count_term)
78-
.unwrap()
79-
}
80-
Output::Dom => {
81-
// if this fails use a bigger sized heap
82-
dom_1::place_frame_with_arguments(&run_arc_process, Placement::Push, count_term)
83-
.unwrap()
84-
}
85-
};
86-
87-
let mut option_return_usize: Option<usize> = None;
88-
89-
loop {
90-
let ran = Scheduler::current().run_through(&run_arc_process);
91-
92-
let waiting = match *run_arc_process.status.read() {
93-
Status::Exiting(ref exception) => match exception {
94-
exception::runtime::Exception {
95-
class: exception::runtime::Class::Exit,
96-
reason,
97-
..
98-
} => {
99-
if *reason != atom_unchecked("normal") {
100-
panic!("ProcessControlBlock exited: {:?}", reason);
101-
} else {
102-
break;
103-
}
62+
heap_size,
63+
|child_process| {
64+
let count_term = child_process.integer(count)?;
65+
66+
match output {
67+
Output::Console => {
68+
69+
// if this fails use a bigger sized heap
70+
console_1::place_frame_with_arguments(child_process, Placement::Push, count_term)
10471
}
105-
_ => {
106-
panic!(
107-
"ProcessControlBlock exception: {:?}\n{:?}",
108-
exception,
109-
run_arc_process.stacktrace()
110-
);
72+
Output::Dom => {
73+
// if this fails use a bigger sized heap
74+
dom_1::place_frame_with_arguments(child_process, Placement::Push, count_term)
11175
}
112-
},
113-
Status::Waiting => true,
114-
Status::Runnable => false,
115-
Status::Running => {
116-
system::io::puts(&format!(
117-
"RUNNING Run queues len = {:?}",
118-
Scheduler::current().run_queues_len()
119-
));
120-
121-
false
12276
}
123-
};
124-
125-
// separate so we don't hold read lock on status as it may need to be written
126-
if waiting {
127-
if ran {
128-
system::io::puts(&format!(
129-
"WAITING Run queues len = {:?}",
130-
Scheduler::current().run_queues_len()
131-
));
132-
} else {
133-
use wait::with_return_0::Error::*;
134-
135-
match wait::with_return_0::pop_return(&run_arc_process) {
136-
Ok(popped_return) => {
137-
option_return_usize = Some(popped_return.try_into().unwrap());
138-
wait::with_return_0::stop(&run_arc_process);
139-
},
140-
Err(NoModuleFunctionArity) => panic!("{:?} doesn't have a current module function arity", run_arc_process),
141-
Err(WrongModuleFunctionArity(current_module_function_arity)) => panic!(
142-
"{:?} is not waiting with a return and instead did not run while waiting in {}. Deadlock likely in {:#?}",
143-
run_arc_process,
144-
current_module_function_arity,
145-
Scheduler::current()
146-
),
147-
Err(NoReturn) => panic!("{:?} is waiting, but nothing was returned to it. Bug likely in {:#?}", run_arc_process, Scheduler::current()),
148-
Err(TooManyReturns(returns)) => panic!("{:?} got multiple returns: {:?}. Stack is not being properly managed.", run_arc_process, returns)
149-
}
150-
}
151-
}
152-
}
153-
154-
option_return_usize.unwrap()
155-
}
156-
157-
#[cfg(test)]
158-
mod tests {
159-
use super::*;
160-
161-
use std::sync::Once;
162-
163-
mod log_to_console {
164-
use super::*;
165-
166-
#[test]
167-
fn with_1() {
168-
start_once();
169-
assert_eq!(log_to_console(1), 1);
170-
}
171-
172-
#[test]
173-
fn with_2() {
174-
start_once();
175-
assert_eq!(log_to_console(2), 2);
176-
}
177-
178-
#[test]
179-
fn with_4() {
180-
start_once();
181-
assert_eq!(log_to_console(4), 4);
182-
}
183-
184-
#[test]
185-
fn with_8() {
186-
start_once();
187-
assert_eq!(log_to_console(8), 8);
188-
}
189-
190-
#[test]
191-
fn with_16() {
192-
start_once();
193-
assert_eq!(log_to_console(16), 16);
194-
}
195-
196-
#[test]
197-
fn with_32() {
198-
start_once();
199-
assert_eq!(log_to_console(32), 32);
200-
}
201-
}
202-
203-
static START: Once = Once::new();
204-
205-
fn start_once() {
206-
START.call_once(|| {
207-
start();
20877
})
209-
}
78+
// if this fails use a bigger sized heap
79+
.unwrap()
21080
}

examples/spawn-chain/tests/web.rs

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ extern crate wasm_bindgen_test;
55

66
use std::sync::Once;
77

8+
use futures::future::Future;
9+
10+
use wasm_bindgen::JsValue;
11+
12+
use wasm_bindgen_futures::JsFuture;
13+
814
use wasm_bindgen_test::*;
915

1016
use spawn_chain::start;
@@ -16,40 +22,38 @@ mod log_to_console {
1622

1723
use spawn_chain::log_to_console;
1824

19-
#[wasm_bindgen_test]
20-
fn with_1() {
21-
start_once();
22-
assert_eq!(log_to_console(1), 1);
25+
#[wasm_bindgen_test(async)]
26+
fn with_1() -> impl Future<Item = (), Error = JsValue> {
27+
eq_in_the_future(1)
2328
}
2429

25-
#[wasm_bindgen_test]
26-
fn with_2() {
27-
start_once();
28-
assert_eq!(log_to_console(2), 2);
30+
#[wasm_bindgen_test(async)]
31+
fn with_2() -> impl Future<Item = (), Error = JsValue> {
32+
eq_in_the_future(2)
2933
}
3034

31-
#[wasm_bindgen_test]
32-
fn with_4() {
33-
start_once();
34-
assert_eq!(log_to_console(4), 4);
35+
#[wasm_bindgen_test(async)]
36+
fn with_4() -> impl Future<Item = (), Error = JsValue> {
37+
eq_in_the_future(4)
3538
}
3639

37-
#[wasm_bindgen_test]
38-
fn with_8() {
39-
start_once();
40-
assert_eq!(log_to_console(8), 8);
40+
#[wasm_bindgen_test(async)]
41+
fn with_8() -> impl Future<Item = (), Error = JsValue> {
42+
eq_in_the_future(8)
4143
}
4244

43-
#[wasm_bindgen_test]
44-
fn with_16() {
45-
start_once();
46-
assert_eq!(log_to_console(16), 16);
45+
#[wasm_bindgen_test(async)]
46+
fn with_16() -> impl Future<Item = (), Error = JsValue> {
47+
eq_in_the_future(16)
4748
}
4849

49-
#[wasm_bindgen_test]
50-
fn with_32() {
51-
start_once();
52-
assert_eq!(log_to_console(32), 32);
50+
#[wasm_bindgen_test(async)]
51+
fn with_32() -> impl Future<Item = (), Error = JsValue> {
52+
eq_in_the_future(32)
53+
}
54+
55+
fn eq_in_the_future(n: usize) -> impl Future<Item = (), Error = JsValue> {
56+
super::eq_in_the_future(log_to_console, n)
5357
}
5458
}
5559

@@ -58,45 +62,60 @@ mod log_to_dom {
5862

5963
use spawn_chain::log_to_dom;
6064

61-
#[wasm_bindgen_test]
62-
fn with_1() {
63-
start_once();
64-
assert_eq!(log_to_dom(1), 1);
65+
#[wasm_bindgen_test(async)]
66+
fn with_1() -> impl Future<Item = (), Error = JsValue> {
67+
eq_in_the_future(1)
68+
}
69+
70+
#[wasm_bindgen_test(async)]
71+
fn with_2() -> impl Future<Item = (), Error = JsValue> {
72+
eq_in_the_future(2)
6573
}
6674

67-
#[wasm_bindgen_test]
68-
fn with_2() {
69-
start_once();
70-
assert_eq!(log_to_dom(2), 2);
75+
#[wasm_bindgen_test(async)]
76+
fn with_4() -> impl Future<Item = (), Error = JsValue> {
77+
eq_in_the_future(4)
7178
}
7279

73-
#[wasm_bindgen_test]
74-
fn with_4() {
75-
start_once();
76-
assert_eq!(log_to_dom(4), 4);
80+
#[wasm_bindgen_test(async)]
81+
fn with_8() -> impl Future<Item = (), Error = JsValue> {
82+
eq_in_the_future(8)
7783
}
7884

79-
#[wasm_bindgen_test]
80-
fn with_8() {
81-
start_once();
82-
assert_eq!(log_to_dom(8), 8);
85+
#[wasm_bindgen_test(async)]
86+
fn with_16() -> impl Future<Item = (), Error = JsValue> {
87+
eq_in_the_future(16)
8388
}
8489

85-
#[wasm_bindgen_test]
86-
fn with_16() {
87-
start_once();
88-
assert_eq!(log_to_dom(16), 16);
90+
#[wasm_bindgen_test(async)]
91+
fn with_32() -> impl Future<Item = (), Error = JsValue> {
92+
eq_in_the_future(32)
8993
}
9094

91-
#[wasm_bindgen_test]
92-
fn with_32() {
93-
start_once();
94-
assert_eq!(log_to_dom(32), 32);
95+
fn eq_in_the_future(n: usize) -> impl Future<Item = (), Error = JsValue> {
96+
super::eq_in_the_future(log_to_dom, n)
9597
}
9698
}
9799

98100
static START: Once = Once::new();
99101

102+
fn eq_in_the_future(
103+
f: fn(usize) -> js_sys::Promise,
104+
n: usize,
105+
) -> impl Future<Item = (), Error = JsValue> {
106+
start_once();
107+
108+
let promise = f(n);
109+
110+
JsFuture::from(promise)
111+
.map(move |resolved| {
112+
let n_js_value: JsValue = (n as i32).into();
113+
114+
assert_eq!(resolved, n_js_value)
115+
})
116+
.map_err(|_| unreachable!())
117+
}
118+
100119
fn start_once() {
101120
START.call_once(|| {
102121
start();

0 commit comments

Comments
 (0)