RT.js is a framework that provides preemptive and prioritized scheduling of JavaScript jobs. JavaScript interpreters execute all jobs (i.e., event handlers) strictly in order and in a run-to-completion semantic. While a job is being executed, there is no way to yield the execution engine before the calculation has finished. Meanwhile, not only the JS-interpreter thread is blocked, but, more importantly, the rendering of the web page is delayed until the interpreter handed back control to the rendering engine. In fact, a JavaScript event handler may busy wait on something and starve all other jobs, the renderer of the browser tab, and the whole web page. We can see the effect of long running computations in a benchmark where computation-intense JS code is competing with UI update code for the interpreter. The benchmark can also be found here.
RT.js tackles this problem by inserting explicit preemption points into long-running JS functions via transpilation (i.e., source-to-source compiling) of the source code. We provide an abstraction to submit such transpiled functions as RT.js jobs with a given scheduling priority or with a relative execution deadline. The RT.js scheduler executes the given jobs according to their relative importance, periodically regains control over the interpreter by the inserted preemption points, and regularly returns to the browser, which then can decide what to do next (e.g., page rendering).
The transpiler achieves preemptability by converting every function marked with the @rtjs
or // @rtjs
decorator to a generator function, meaning that the run-time system allows returning from that function prematurely and re-entry into that function.
The run-time system keeps the state of the function and the local objects.
Preemption points are added by inserting yield
-statements on the following occasions:
- before
while
andfor
loop bodies, i.e. on every iteration of the loop - before function calls
- before
if
statements
Both Mozilla and Google suggest either not using the main-thread (i.e. creating web workers for complex computations, with their own drawbacks) or splitting up the computations on the main thread. RT.js does that automatically and allows you to prioritize your work load. Compute intensive tasks can be offloaded to be executed when the RT.js scheduler runs next, whereas small event handlers can still be run without using the services of RT.js.
RT.js: Practical Real-Time Scheduling for Web Applications. Christian Dietrich, Stefan Naumann, Robin Thrift, Daniel Lohmann. Proceedings of the 40th IEEE Real-Time Systems Symposium 2019. IEEE Computer Society Press. 2019
src
contains the main source files for the runtime and transpiler (source to source compiler).src/transpiler
contains the transpiler code.benchmarks
contains a number of benchmarks of different kinds.benchmarks/qualitative
is the macro benchmark from the RTSS publication. It contains a website running in a browser showcasing RT.js against the default run-to-completion policy of the JavaScript run-time engine. (you'll needplotly-base
to use that benchmark).benchmarks/minimal
contains a small example project that uses RT.js to introduce pseudo-preemptivity.
After building the transpiler (see below), the build
-folder exists, containing the transpiled RT.js library and the transpiler, ready for use.
npm install
: Install all required dependencies (especially TypeScript).make build
: Build the RT.js transpiler. Please note: The benchmarks will build the transpiler as an dependency.make docs
: Generate a browsable version of the API docs in ./docs
To allow for scheduling all tasks must inherit from the Task
-class and override the run
-method:
import { Task } from "Task"
class MyTask extends Task {
// @rtjs
*run() {
var i = 0;
while (i < 1000000) {
i++;
}
// implement your task here
}
}
Note that every method that should be transformed needs to be prefixed by the @rtjs
-decorator. Functions can also be made schedulable and preemtible by simply prefixing them with the same @rtjs
-decorator (or inside a comment for pure JavaScript // @rtjs
).
To transpile the code use the rtjs-transpiler.js
tool in build/transpiler
, e.g.:
build/transpiler/rtjs-transpiler "MyTask.js"
cat build/MyTask.js
NOTE: Currently the output directory is always
build