plasma-umass / doppio

Breaks the browser language barrier (includes a plugin-free JVM).
http://plasma-umass.github.io/doppio-demo
MIT License
2.17k stars 176 forks source link

Run the JVM in a WebWorker #127

Closed jvilk closed 8 years ago

jvilk commented 11 years ago

Doppio currently runs in the main JavaScript thread, which causes the browser to be less responsive than desired. While we could "fix" this by yielding the thread more often, this comes with a speed penalty.

For browsers that support it, we should run the JVM in a WebWorker. Note that we are not mapping threads to WebWorkers -- simulating shared memory would be too slow / cumbersome / difficult -- we are mapping JVM instantiations to WebWorkers.

The main JavaScript thread would be responsible for checking for and handling messages sent from the WebWorker for various events, such as:

For browsers that do not support WebWorkers (e.g. IE before 10), we can just simulate WebWorkers in the main JavaScript thread. To improve those web browsers' responsiveness, we could just yield the JavaScript thread after a certain duration of time (easy enough to do in the interpreter).

This should not impact the Node JS frontend. It will just impact how the browser frontend runs the JVM, how the node emulation requests localStorage files, how printing to/receiving input from the console works, and any other browser-specific features that we add to Doppio in the future.

perimosocordiae commented 11 years ago

Current roadblock:

The worker lives in its own scope, separate from anything included in index.html, so we'll need to manually include what we need with importScripts. This is a little annoying, especially because it means we need to know whether to include things separately (in dev mode) or as one chunk (in release mode).

More importantly, we can't import the browser/node.js code directly, because it depends on having access to window. However, messages between the main thread and the worker are all asynch, so it'll be a pain to rewrite all the node.fs.readFileSync calls. The calls to node in src/natives.coffee will also need attention, I suspect.

jvilk commented 11 years ago

I was hoping that WebWorkers would have a way of polling for new messages, but a quick Google didn't find anything. Hmm.

As for the window issue, I think you can just define things on self, which is the WebWorker global scope. I actually think that self aliases to window in the main JavaScript thread, too... :)

For the file system, the easy way out is to completely replace the synchronous API with an asynchronous API that abuses setTimeout. You will have to somehow propagate a callback up to the localStorage code so that it can be put into the message sending callback, though (or maybe do a nasty hack involving a variable in a higher scope rather than changing all of those functions; then I can later refactor the hack).

The localStorage code can have an if/else statement that uses the existing code if we are not in a WebWorker.

These changes should preserve compatibility with Node and IE.

perimosocordiae commented 11 years ago

I disabled the localStorage-based filesystem stuff in node.coffee (see commit 8201078eb57111eb5d53e0e9c5f0d3d9f0877d73) and after some effort, everything works! I'm frankly amazed.

Known issues:

Being able to scroll the page while Doppio runs is super fun. :hotsprings:

jvilk commented 11 years ago

Being able to scroll the page while Doppio runs is super fun.

Or, in the case of Firefox, being able to use the web browser while Doppio runs.

jvilk commented 11 years ago

I've been working on this for ( sigh ) a few days now. CJ's initial effort worked, but had one critical issue: The filesystem lived in the JVM. This meant that it could not use localStorage, and was not in "sync" with what the terminal saw.

If we kept the filesystem in the frontend, we would have to switch over to asynchronous file operations in the JVM. If we put the filesystem in the JVM, we would have to switch over to asynchronous file operations in the frontend and in the JVM, since the JVM would need to use messages to get at localStorage content.

Naturally, we decided to keep the filesystem in the frontend. For now, frontend will still use the synchronous file API, since our virtual filesystem is still blocking. At some point, we'll rewrite/refactor it to be asynchronous, which should hopefully make it more amenable to eventual Dropbox/GDrive/etc. integration. :) I'll create an issue for that.

There are two daunting subtasks on the road to WebWorker-ization that can be done in master before we hook up magical message passing. I will create an issue for each of these.

  1. Switch over to the asynchronous node API for all native methods.: I have already done this (but it's untested :X). Note that we do not need to use the asynchronous API for certain methods that operate on file descriptors, since those changes are local until they are committed when the descriptor is closed (e.g. fstatSync, read, write...). This also prevents the overhead of having to de-construct and re-construct the Doppio file object.
  2. Make class_lookup asynchronous: This is unavoidable, since read_classfile needs to read a file asynchronously. This is going to be a painful change, as we currently call class_lookup fairly frequently. During post-change optimization, we will likely want to introduce more caches.

Once those are done, my local WebWorker changes (which I dare not commit to the WebWorker branch yet, as the JVM is totally borked) should be able to integrate with master nicely.

perimosocordiae commented 11 years ago

Now that we're using BFS, the synchronicity issues we were having before should be no problem. This issue will probably need to wait until we're done with the Typescript conversion, though, for our sanity's sake.

jvilk commented 11 years ago

Totally agree. Note that this will also complicate any Swing work, but we can deal with that later.

EDIT: Note that we will need to add a WebWorker proxy interface to BFS for this.

jvilk commented 11 years ago

The doppio library is separate from the frontend now, so we could potentially do this now. Here's what would be required:

ghost commented 9 years ago

What is the status of "in web worker" runtime? can we now run doppio solely from a web worker? We do not need the front end part. Probably a separate question but, is it possible to preload java.lang, java.util, and java.io packages?

bpowers commented 9 years ago

I'm not sure right now, but I'll be looking at this in the next few weeks

jvilk commented 9 years ago

I have no reason to believe that this would not work. This issue refers to exposing a concise API for invoking DoppioJVM in a WebWorker (e.g. new Doppio({webworker: true})). Right now, DoppioJVM should work in a WebWorker, but there's no plug-and-play API to make it simple.

ghost commented 9 years ago

I think there is no need for an extra API. Just a simple postMessage/onmessage communication protocol would be enough to invoke DoppioVM. A front end API should not be a focal point of DoppioVM. Let the developers find their own way on that front.

jvilk commented 8 years ago

DoppioJVM works just fine in a webworker; I didn't even need to change anything. I just made a commit that switched to using WWs for testing, so I'm closing this out.

The only issue currently is a (throughput-based) performance regression in a WebWorker compared with the main thread, which I'm actively investigating.

ghost commented 8 years ago

We can run DoppioVM only in a web worker because the main thread is already busy with tons of other stuff. Could you open another issue for the performance regression so that we can follow the imrpovements on that? just a thought :).

jvilk commented 8 years ago

Sure, I'll do that. I'd also appreciate stars on this Chrome issue, which seems to be the culprit in Chrome:

https://code.google.com/p/chromium/issues/detail?id=344814&can=5&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Cr%20Status%20Owner%20Summary%20OS%20Modified

I haven't checked to see if performance differs in other browsers yet.