RT.js: Practical Real-Time Scheduling for Web Applications

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 could 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:

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.

Example Code

In the RT.js framework a Task structure is used, where the run method is overridden.

// @rtjs
function example () {
    for ( var a = 0; a < 100; a++ ) {
        // ...
        if ( a == 42 ) {
            // ...
        }
    }
    foobar ( 42 );
}

After the transpilation, the function is converted to a generator and yield-statements with their budgets are inserted into the code. For a complete minimal example, see the minimal example in the github-repository.

// @rtjs
function* example() {
    if (--this.rtjs.scheduler.RTJS_BUDGET <= 0) {
        yield this.SchedulerMessage.Continue;
    }
    for (var a = 0; a < 100; a++) {
        if (--this.rtjs.scheduler.RTJS_BUDGET <= 0) {
            yield this.SchedulerMessage.Continue;
        }
        // ...
        if (a == 42) {
            // ...
        }
    }
    if (--this.rtjs.scheduler.RTJS_BUDGET <= 0) {
        yield this.SchedulerMessage.Continue;
    }
    foobar(42);
    return this.SchedulerMessage.TerminateTask;
}

People

Publication

RTSS Conference A*
RT.js: Practical Real-Time Scheduling for Web Applications
Christian Dietrich, Stefan Naumann, Robin Thrift, Daniel LohmannProceedings of the 40th IEEE Real-Time Systems Symposium 2019IEEE Computer Society Press2019.
PDF 10.1109/RTSS46320.2019.00017 [BibTex]

Theses

bAUTOSAR: A Javascript Real-Time Abstraction for Improved Website Responsiveness

An OSEK-like real-time abstraction for prioritized execution of Javascript programs.

 
Typ
Bachelorarbeit

 
Status
abgeschlossen

 
Supervisors
Christian Dietrich
Daniel Lohmann

 
Bearbeiter
Robin Thrift (abgegeben: 15. Mar 2019)