Simulating context switches in JavaScript?

4.5k views Asked by At

I've been working on implementing a pretty complex system in JavaScript that needs to simulate, among other things, multithreaded processes. In a real multithreaded process (such as a kernel thread) it's possible to switch between threads by context-switching. This works because you can store the current process's program counter and registers to a temporary structure, restore the program counter and registers for some other process, and then resume where you left off in the previous process.

I'm curious whether it's possible to have something similar to this in JavaScript. I currently know no way of doing this and so have been designing the system using cooperative multitasking. In particular, any "function" that I want to run in the multithreading simulator is split up into an array of functions. To execute the "function", I iterate across the array of functions, executing each in order while maintaining a "program counter" of which function to execute next. This allows me to simulate a context switch by calling one of the functions in the array, waiting for the function to return, then switching to some other array of functions that need to be executed.

My current approach works, but it's difficult to write code in this system. Each function has to indicate specifically when it can be interrupted, and because the functions in the array are all separate the logic for communicating data between different parts of the function is complicated. I was hoping to get something closer to preemptive multitasking working instead.

My question is: is it possible to run an arbitrary JavaScript function in a way where it can be suspended and resumed by an external source?

2

There are 2 answers

4
qwertymk On BEST ANSWER

Check StratifiedJS out

7
Raynos On

First it's important to mention that JavaScript is completely single threaded. Emulating multi-threading is really not the way to go. Your far better off relying on eventloops.

As mentioned web workers could be used but there's not really a cross-browser compliance for them so I'll ignore web workers. Also you can't do any DOM manipulation with Web workers.

I would say take a look at node.js to reasoning behind why event loops are a good alternative to multi-threading. I believe he touches quite nicely on why it's a good alternative in this video.

So rather then having an array of functions and iterating over them you can instead create an event and bind a set of functions to them and trigger said event. A very lightweight implementation of events can be found in backbone.js.

You can't just pause a thread in JavaScript because there is only one. There's no way to suspend or resume a function without a function have points in it.

There is only one way to emulate this. Write a JavaScript parser that rips your nicely build JavaScript apart and builds up a system that allows you to suspend and resume your JavaScript.

Take for example this function

function(i) {
    j = i + 1;
    console.log(j);
    return foo(j);
}

and converts it into this

var bar = function(i) {
    var r = {};
    var j = i + 1; 
    var f = function() {
         console.log(j);
         var g = function() {
              return foo(j);
         };
         onNext(g, arguments.callee, this, r);
    };
    onNext(f, arguments.callee, this);
    return r;
}

your going to need to extend Function with .suspend and .resume

Function.prototype.suspend = function() {
     this.__suspended = true;
}

Function.prototype.resume = function() {
     this.__suspended = false;
}

function onNext(callback, function, context, returnObj) {
     if (!function.__suspended) {
          var cb = function() {
              Backbone.Events.unbind("run", cb);
              returnObj.r = callback.call(this);
          }
          Backbone.Events.bind("run", cb);
     }
}

setInterval(function() {
     Backbone.Events.trigger("run");
}, 5);

Also your going to have to replace all references to var a = b() with

callFunctionAsync(b, context, args, function(return) {
    var a = return;
    ...
});

I'll leave implentation upto you. For now all functions return an object r and only when r.r is set to a value has it "returned". So just check around the event loop whether it has "returned" yet by checking if r.r is set and if it has trigger the function async callback.

Hey and look what we've got. Were emulating threads by running them round the event loop. It's a lot better to use the event loop natively in your code rather then emulating threads through it.

Basically get your function to run the next line of its code when you go round the event loop again. And check whether the particular "function" is suspended or resumed when you go round your eventloop.

I didn't implement bubbling the return of the functions back up to the "function" for sake of brevity. This shouldn't be too hard to emulate.

Either use the event loop directly or use fake threaded methods and get a compiler to compile your code so that it doesn't look hideous when you code it.

If you cause dead-locks good luck to you.