Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Single Rhino Context in MultiThreaded Environment

1,636 views
Skip to first unread message

Patrick Dobbs

unread,
Jan 20, 2009, 10:59:33 AM1/20/09
to
Hi,

I'm hoping for some clarification. In common with a lot of projects that
seem to be cropping up in this newsgroup, I've got a rhino shell session
starting up a webserver and then registering a javascript handler for
http requests.

However, a shell session presumably creates one Rhino Context. And there
can only be one Thread per Context (or vice versa). So what happens when
a Rhino Shell session (single threaded) starts up a multithreaded web
server, and that webserver then calls into Rhino?

I'm assuming that this model (Rhino script invoking its own webserver)
is inappropriate for production environments for this reason, and I'm
planning on only using this mode for development. However, I would like
to improve my understanding of this situation.

Thanks

Patrick

Norris Boyd

unread,
Jan 21, 2009, 9:40:27 PM1/21/09
to
On Jan 20, 10:59 am, Patrick Dobbs <patrick.do...@supplierselect.com>
wrote:

The typical model is for each request in the web server to enter a
Context and then execute a script. This way you can have multiple
requests being processed concurrently in different threads.

--N

Patrick Dobbs

unread,
Jan 22, 2009, 1:32:23 AM1/22/09
to
Norris Boyd wrote:
> On Jan 20, 10:59 am, Patrick Dobbs <patrick.do...@supplierselect.com>
> wrote:
>> I've got a rhino shell session
>> starting up a webserver and then registering a javascript handler for
>> http requests.
>>
>> However, a shell session presumably creates one Rhino Context. And there
>> can only be one Thread per Context (or vice versa). So what happens when
>> a Rhino Shell session (single threaded) starts up a multithreaded web
>> server, and that webserver then calls into Rhino?
>>
>> Thanks
>>
>> Patrick
>
> The typical model is for each request in the web server to enter a
> Context and then execute a script. This way you can have multiple
> requests being processed concurrently in different threads.
>
> --N

Sorry, my question was rubbish. We currently have a java web application
(Spring, Freemarker etc). We use Rhino in the way you describe, setting
up a new Context for each request, adding some Host objects for the
Http Request and Response etc.

However, for a related project it would be useful if we could start and
run a webserver from within Javascript (e.g. start the Rhino shell, load
a script and call Server.run()).

So, I guess my question is, can a single running script serve multiple
threads?

My understanding is "no", because a Context will reject multiple
threads, but that it would be possible for the script itself to do
something along the lines of the shell function spawn(). Are there
any more detailed examples of using spawn() other than the example
in the javadoc?

Thanks

Norris Boyd

unread,
Jan 22, 2009, 8:55:37 AM1/22/09
to
On Jan 22, 1:32 am, Patrick Dobbs <patrick.do...@supplierselect.com>

Ah, I understand better what you're asking.

If Rhino is called from a thread that does not have a Context
associated with it, it will automatically perform the association. For
example:

js> var runnable = new java.lang.Runnable(
> { run: function () { print("\nrunning"); } }
> );
js> var thread = new java.lang.Thread(runnable);
js> thread.start();
js>
running

You should be able to do something similar with a web server if you
can get the server to call your JavaScript on different threads.

--Norris

Patrick Dobbs

unread,
Jan 22, 2009, 11:21:22 AM1/22/09
to

Great, thanks. So does this imply that a new Context is instantiated for
the new thread, at this then runs in the same Scope? Or does the same
Context switch between threads?

Norris Boyd

unread,
Jan 22, 2009, 2:39:30 PM1/22/09
to
On Jan 22, 11:21 am, Patrick Dobbs <patrick.do...@supplierselect.com>

There's a new Context instantiated for the new thread (Contexts are
always 1-1 with Threads). The Scope is shared.

--N

Patrick Dobbs

unread,
Jan 22, 2009, 3:50:08 PM1/22/09
to

I've done some very simple tests which seem conclusive. On the chance
that this is of interest to someone I thought I'd post the results.
Apologies if this is blindingly obvious stuff to the readership of this
list.

I opened a Rhino shell session and typed this:

-----------
js> java.lang.Thread.currentThread().getId()
1
-----------

Then I loaded a script with some functions for managing an http server (
http://www.simpleframework.org ), and which registers some javascript
function handlers for http requests. Then I started the server:

js>simpleserver.start()
Simple Server started on port 8080

The javascript function handler was this:

threadIdHandler : function(request, response){
var tid = java.lang.Thread.currentThread().getId();
response.write("thread id: "+tid);
},

Opening a couple of browsers and refreshing the pages shows:

--------------------
thread id: 56
thread id: 58
thread id: 62
--------------------

The server maintains a Thread pool for handling requests, and it seems
that these threads are pushing through the current Rhino Scope. Going
back to commandline:

-----------
js> java.lang.Thread.currentThread().getId()
1
js> java.lang.Thread.currentThread().getId()
1
-----------

So, it would seem that Rhino supports multithreading "out of the box"
when used to start up an http server and register javascript handlers.

Norris wrote:
>>> You should be able to do something similar with a web server if you
>>> can get the server to call your JavaScript on different threads.

Relating this to Norris's comment, it would seem that just registering a
javascript handler (servlet or whatever) has this effect.

Thanks to Norris for his pointers. Assuming these interpretations are
correct, I'll shut up now.

Patrick

George Moschovitis

unread,
Jan 23, 2009, 6:05:56 AM1/23/09
to
OK, Rhino seems to support multithreading (ie multiple Contexts). What
about scopes?
Do all the threads use the same scope?

-g.

On Jan 22, 10:50 pm, Patrick Dobbs <patrick.do...@supplierselect.com>


wrote:
> Norris Boyd wrote:
>
> Thanks to Norris for his pointers. Assuming these interpretations are

> ...

Patrick Dobbs

unread,
Jan 23, 2009, 6:55:02 AM1/23/09
to
My understanding is that if you start the webserver from Rhino, then
there is only one Scope which is shared between Threads/Contexts.

I'm currently adding some logging / debugging statements to the Rhino
codebase to check what is going on (ie when Contexts are getting
created). I'll post again if my findings suggest anything different.

Tom Robinson

unread,
Jan 23, 2009, 8:45:28 AM1/23/09
to
I'm doing pretty much the same thing (starting up Jetty/Simple within
the Rhino shell) in the Jack project (like Ruby's Rack for JavaScript:
http://github.com/tlrobinson/jack). I've briefly tried some concurrent
requests, and as far as I can tell there's no problem, it just works.
Multiple concurrent requests use the same scope, and I assume
different contexts as you've mentioned.

Also, I just added Simple support to Jack:
http://github.com/tlrobinson/jack/tree/master/lib/jack/handler/simple.js

You should consider using Jack ;)

It lets you easily run your applications on a number of web servers
(right now it supports Simple and servlet containers, including
Jetty), and use various pieces of middleware.

It's brand new, but it's quite simple. The whole thing, including a
bunch of middleware and all the adapters, is less than 1000 LOC. Like
Rack and WSGI it's mostly just a protocol, but for JavaScript.


On Jan 23, 3:55 am, Patrick Dobbs <patrick.do...@supplierselect.com>
wrote:

Patrick Dobbs

unread,
Jan 23, 2009, 9:15:21 AM1/23/09
to
Tom Robinson wrote:
> I'm doing pretty much the same thing (starting up Jetty/Simple within
> the Rhino shell) in the Jack project (like Ruby's Rack for JavaScript:
> http://github.com/tlrobinson/jack). I've briefly tried some concurrent
> requests, and as far as I can tell there's no problem, it just works.
> Multiple concurrent requests use the same scope, and I assume
> different contexts as you've mentioned.

I put some debugging lines into

static final Context enter(Context cx, ContextFactory factory)

in org.mozilla.javascript.Context

This method is called, and a new Context created for each http Request /
Thread. It is also called when the same Thread (identified by Id) is
called for subsequent requests, which I guess indicates that the server
must scrub each Thread from the pool after a request is serviced.

Seems to me that this setup works pretty well.

> You should consider using Jack ;)

Looks excellent! Will do. And yes, more or less what I've been doing.

Robert Koberg

unread,
Jan 23, 2009, 9:30:29 AM1/23/09
to dev-tech-js-...@lists.mozilla.org
>
> Tom Robinson wrote:

>> http://github.com/tlrobinson/jack

Briefly reading the above link, I see:

"From the root of the project, run "bin/jackup", which
will start a sample application at localhost:8080:

./bin/jackup"

I really believe this should be ./bin/jackon

and of course there should be a stop script

Patrick Dobbs

unread,
Jan 23, 2009, 9:55:21 AM1/23/09
to

./bin/jackup works for me. Unless of course the problem is that "jackup"
is considered morally troublesome. If so, the logical antonym to
"jackon" is surely even more disturbing?

Robert Koberg

unread,
Jan 23, 2009, 10:02:16 AM1/23/09
to Patrick Dobbs, dev-tech-js-...@lists.mozilla.org

I should have included a smiley :) it was meant as a joke - not serious

best,
-Rob

George Moschovitis

unread,
Jan 27, 2009, 11:41:07 AM1/27/09
to
Two questions:

- using the same scope for all threads isn't problematic?
In my current servlet I keep a scope per thread (using a thread
local variable). I think this better isolates the threads from each
other. Is this unnecessary?
- is Simple actively maintained?


-g.

Attila Szegedi

unread,
Jan 27, 2009, 12:29:37 PM1/27/09
to George Moschovitis, dev-tech-js-...@lists.mozilla.org
You'd rather have one top-level scope per script execution. Otherwise,
all threads would share their top-level variables (incl. JS built-in
objects).

What you can do - if you really want and are worried about performance
of initializing standard objects for each script execution - is to
create one scope with initStandardObjects(), seal it, and then use it
as the prototype of the per-thread scopes; that way, all thread-
specific top-level scopes will inherit the standard objects (String,
Number, Date, Error, RegExp, etc.) through the prototype scope. That
still means threads share the standard objects, but unless they mutate
them somehow, you should be safe. However, top-level variables won't
be shared, and that's crucial.

Attila.

George Moschovitis

unread,
Jan 27, 2009, 3:10:10 PM1/27/09
to
> You'd rather have one top-level scope per script execution. Otherwise,  
> all threads would share their top-level variables (incl. JS built-in  
> objects).

that's what I thought.

> What you can do - if you really want and are worried about performance  
> of initializing standard objects for each script execution - is to  
> create one scope with initStandardObjects(), seal it, and then use it  
> as the prototype of the per-thread scopes;

again that's what I am doing, all the thread scopes use the same
'sharedScope' as prototype to conserve some memory.

thank you,
George.

Patrick Dobbs

unread,
Jan 27, 2009, 5:58:23 PM1/27/09
to
George Moschovitis wrote:
>> You'd rather have one top-level scope per script execution. Otherwise,
>> all threads would share their top-level variables (incl. JS built-in
>> objects).
>
> that's what I thought.

I guess the key thing here is what is meant by "script execution" in the
context of http requests. The way we've used Rhino previously was to
execute a script once per http request, whilst having a persistent top
level scope to avoid the cost of initStandardObjects().

However, we're now trying a different approach with one long running
rhino script / scope thing. The http server calls a rhino function in
this scope from Java land, and the rhino function writes to the
response output stream. By long running I mean the equal to the life of
parent http server. Actually, longer, since the javascript starts the
http server itself.

Rhino creates a new Context for each http request, automatically, I'm
guessing this is because http servers ensure that each thread is
scrubbed before being reused for another http request.

This approach implies that the javascript must watch out for thread
safety (instance variables etc) but that's the same as for Servlet java.

Assuming that the javacript is trusted, is there anything *wrong* with
this setup?

Patrick

George Moschovitis

unread,
Jan 28, 2009, 3:38:38 AM1/28/09
to
> Assuming that the javacript is trusted, is there anything *wrong* with
> this setup?

The JavaScript may be trusted, but perhaps there is a bug (or two) in
the code that can potentially corrupt other threads (ie other users
interaction with the system)
Moreover, it is very hard to write thread safe code, I prefer to keep
the threads as isolated as possible (and I *think* a separate scope
helps here).


-g.

Attila Szegedi

unread,
Jan 28, 2009, 7:37:25 AM1/28/09
to Patrick Dobbs, dev-tech-js-...@lists.mozilla.org
This sounds good to me -- the output stream is localized to the
function invocation, so there's no unwanted crosstalk between threads.

Rhino native objects are actually threadsafe AFAIK (I actually hold
the view that they shouldn't be - most usages are single-threaded and
why pay the price when it's not needed, right?) so even if you were to
write the same property on a single object from multiple threads
without synchronization, the result would not be deterministic (of
course), but it would end up being exactly one of the values the
threads wrote, and they wouldn't corrupt the object internal state
itself.

So, if you additionally take care of proper synchronization in your
scripts, you're good.

As for new Contexts being created -- Rhino contexts are put into a
thread local that is stored as a static field private to the Context
class. There's no way for an outside code to clear that thread local
("scrub the thread" as you say). You're probably seeing new threads
being created. If you don't call Context.exit() on a thread, then the
Context object will remain alive and associated with that particular
thread.

Attila.
--
twitter: http://twitter.com/szegedi

0 new messages