I appreciate this is annoying, but it's not actually that easy. The
contexts are not really independent at the VM level. The
single-threaded assumption is in 1000 undocumented places in the code.
Let me suggest some workarounds. I am guessing that you don't have
1000 CPUs. So it might be possible to have one or two V8 processes
per CPU, then a load balancer that distributes incoming requests to V8
proceses. If you run with preemption support you can switch between
V8 threads if one thread gets into a long calculation. If your V8
threads are hanging because they are doing long-running IO then you
can use the v8::Unlocker class to free up the V8 lock while IO takes
place.
--
Erik Corry, Software Engineer
Google Denmark ApS. CVR nr. 28 86 69 84
c/o Philip & Partners, 7 Vognmagergade, P.O. Box 2227, DK-1018
Copenhagen K, Denmark.
Yes, you need a new V8::Locker. Alternating V8::Locker and
V8::Unlockers can be stacked up on the stack.
> void foo()
> {
> v8::Locker locker;
> some_code.Run(); // this javascript code calls cpp_foo ()
> // release lock on destructor call
> }
>
> Handle<Value> cpp_foo(const Arguments& args) // called from V8
> {
> string abc = to_string(args[0]);
>
>
> {
> // okay, since this moment let's forget about V8
> v8::Unlocker unlocker;
> sqlengine.execute(abc);
> } // release unlock on destructor call
>
> // now I can again use V8? And I can run another javascript code?
>
> some_else_code.Run();
>
> }
>
> Is this right?
This all looks right. If you use v8::Locker anywhere you have to use
it everywhere, so the some_else_code.Run() must also be in a
v8::Locker scope.
2009/9/10 krovosos <ggrig...@gmail.com>:> void foo()
> {
> v8::Locker locker;
> some_code.Run(); // this javascript code calls cpp_foo ()
> // release lock on destructor call
> }
>
> Handle<Value> cpp_foo(const Arguments& args) // called from V8
> {
> string abc = to_string(args[0]);
...
> some_else_code.Run();
>
> }
>
This all looks right. If you use v8::Locker anywhere you have to use
it everywhere, so the some_else_code.Run() must also be in a
v8::Locker scope.
Yes, you are right. I wasn't paying attention to the braces.
You can checkout node (http://tinyclouds.org/node) which can do
concurrent I/O on V8.
Handle<Value> yield(const Arguments& args)
{
v8::Unlocker unlocker;
Sleep(5); // for example
// or even Sleep(0) just to give away control of v8 mutex
}
Or maybe every iteration if speed if not an issue for this long-
running in background thread.
Than other scripts in different threads can still run? Am I right?
"setTimeOut" is an interesting choice for the name of the function.
Browser-based JS environments also have a call with this name, but it
implements something rather different. In the browser there is only
one thread and you have to return from the current JS invocation in
order for new events (mouse clicks, timeouts, etc.) to trigger new JS
invocations. Implementing the setTimeOut function that is known from
browsers would not involve threads.
From the programmers point of view the concurrency model is rather
different. In the browser implementation the functions registered
with setTimeOut can only run when the previous bit of JS code
relinquished control.
On a slightly different note, people on this thread may be interested
in the preemption support that is built into d8. Implementing
something similar in your libv8-using program would allow JS code in
tight loops to be interrupted and other threads to be run even without
explicit flag polling in the JS or callback code. Since there are no
locks or other synchronization primitives available in JS this model
requires separate contexts per thread or a lot of very careful thought
about asynchronous operations at the JS level.
In a browser the way it works is that your JS code is called in
response to some event. That can be a timeout, registered earlier or
it can be an event (onMouseDown, onPageLoaded, I don't remember the
exact names). You relinquish control by returning from the callback.
That's pretty explicit. It's a fairly lame programming model, but it
has the advantage that you don't have to think about concurrency.
Other JS code can only run when you let it by returning at the top
level you were called.
> required to rely on engine-specific definitions of "when is control handed
> over". AFAIK, in v8 this check is done when functions are called (or only
> native functions???). Are there other places where v8 checks whether it
> should give control to another thread?
V8 can check for preemption on a function call (non-native) or a
backwards loop branch. But the support for this is almost unused in
the browser (it's used for the debugger). If you use preemption then
it can be used to switch to a different thread, but there are no V8
threads in the browser. This support is not used for setTimeOut in
the browser because the timeout callbacks are run in the same one
thread that other events also run in.
>> With your implementation you can potentially
>> get another JS call running whenever a callback to C++ code occurs.
>
> Yes, that's what i was hoping for :). However, i wasn't aware that this was
> different from what browsers do. Since any use of setTimeout() is, by
> nature, subject to race conditions
Not if you implement setTimeout in the way a browser does.
>> On a slightly different note, people on this thread may be interested
>> in the preemption support that is built into d8. Implementing
>> something similar in your libv8-using program would allow JS code in
>> tight loops to be interrupted and other threads to be run even without
>> explicit flag polling in the JS or callback code. Since there are no
>> locks or other synchronization primitives available in JS this model
>> requires separate contexts per thread or a lot of very careful thought
>> about asynchronous operations at the JS level.
>
> i found the StartPreemption(someNumericType) (or something like that)
> function, but i'm not at all clear what it does. The API docs say only the
> obvious: "starts preemption", but doesn't tell us what that really means. Is
> it to be run once from each thread, once globally, or what? My assumption
> that it is a global toggle which tells v8 to check ever N millis to see if
> it should hand control to another thread, but that's just a guess.
That's pretty much right. The checks are only performed when running
JS code. If you are running C++ code then it is the responsibility of
the embedder to use V8::Locker and V8::Unlocker to let other threads
run. As ever if the documentation is lacking (and it is) then the
uses of the function in the tests and in the shell and d8 can shed
light on their function.
In a browser the way it works is that your JS code is called in
response to some event. That can be a timeout, registered earlier or
it can be an event (onMouseDown, onPageLoaded, I don't remember the
exact names). You relinquish control by returning from the callback.
That's pretty explicit. It's a fairly lame programming model, but it
has the advantage that you don't have to think about concurrency.
Other JS code can only run when you let it by returning at the top
level you were called.