Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run the JVM in a WebWorker #127

Closed
jvilk opened this issue Jan 8, 2013 · 15 comments
Closed

Run the JVM in a WebWorker #127

jvilk opened this issue Jan 8, 2013 · 15 comments

Comments

@jvilk
Copy link
Member

jvilk commented Jan 8, 2013

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:

  • Printing to the console.
  • Requesting a prompt for input.
  • Requesting a file from localStorage.
  • Saving a file to localStorage.
  • Any other future events that require access to the DOM (e.g. Doppio classes that allow for webpage manipulation).

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 added a commit that referenced this issue Jan 10, 2013
currently doesn't work, due to scope issues (browser/node assumes the
existence of 'window')

also only supports dev mode, for the moment
@perimosocordiae
Copy link
Contributor

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
Copy link
Member Author

jvilk commented Jan 10, 2013

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
Copy link
Contributor

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

Known issues:

  • Still only works in dev mode. Getting release mode working shouldn't be too hard, with a little cleverness.
  • Still doesn't support the LocalStorageSource, so file upload and editing is essentially a no-op.

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

@jvilk
Copy link
Member Author

jvilk commented Jan 11, 2013

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.

@ghost ghost assigned jvilk Jan 21, 2013
@jvilk
Copy link
Member Author

jvilk commented Jan 21, 2013

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.

jvilk pushed a commit that referenced this issue Feb 6, 2013
…ion are now asynchronous. As a result, many core JVM operations that previously relied upon having a 'Type' object could no longer be used synchronously because they invoked class lookup functions. In addition, using 'Type' objects for class lookups is ambiguous; classes are defined by their name AND the class loader that loaded them.

To fix this, I changed most of these core methods to take ClassFile objects, which is currently the definitive object for reference types.

We are moving toward #116; we will soon move away from passing around Type objects and toward passing around ClassFile objects, which has become the standard representation of Java classes. Once we start stashing ClassFile objects in StackFrames for easy access from opcodes and such, we can stop using types in a large number of places.

In a move to fix #124, I have changed the function signature for class_lookup / load_class / define_class / jclass_obj to include the class that triggered the lookup / load / etc. This is used to determine the correct ClassLoader to invoke class loading. This is *not* hooked up at the call sites yet since we do not have ClassFiles there to pass in, but we eventually *will* hook those up, which should fix this issue.

For #127, as mentioned all class loading is performed in an asynchronous manner. I believe this is the last difficult internal JVM matter that we needed to convert for WebWorker support. I'll be able to port over my frontend changes once the internal details stabilize.

I believe #138 is fixed? I need to grep through the code to ensure we no longer use any synchronous node API calls, except for those that work on file descriptors (since those do not invoke file loading/closing).

There are a number of new "hacks" denoted with "XXX". Many of these have to do with ClassLoading, which will be fixed once we move toward actually having ClassLoader objects that handle that abstraction. I'll open an issue for that.

A few native methods have become a little crufty, especially those related to reflection. Some have to manipulate the stack while in an async_op, and I had to carefully orchestrate their stack changes such that they do not interfere with what async_op does. I may want to consider making a better helper function for these complex tasks, but I suppose that should wait until we have proper Thread objects that have a nice API for manipulating the stack.

Other than that, things are looking positive. We should move toward eliminating Type objects and cleaning up the code.
@perimosocordiae
Copy link
Contributor

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
Copy link
Member Author

jvilk commented Aug 9, 2013

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
Copy link
Member Author

jvilk commented Sep 6, 2013

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

  • BrowserFS needs to properly support proxying file system requests across the WebWorker boundary.
  • Doppio needs to proxy console input across the WebWorker boundary.
  • Doppio still needs to operate properly in a non-WebWorker environment. This is easy to do if the input/output abstractions (for file system and console interactions) use the same APIs.

@jvilk jvilk removed their assignment Apr 17, 2014
@ghost
Copy link

ghost commented Oct 20, 2015

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
Copy link
Member

bpowers commented Oct 20, 2015

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

@jvilk
Copy link
Member Author

jvilk commented Oct 20, 2015

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
Copy link

ghost commented Oct 20, 2015

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
Copy link
Member Author

jvilk commented Dec 15, 2015

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.

@jvilk jvilk closed this as completed Dec 15, 2015
@ghost
Copy link

ghost commented Dec 20, 2015

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
Copy link
Member Author

jvilk commented Dec 20, 2015

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants