How to push a task to Nodejs' V8 main thread ?

1,595 views
Skip to first unread message

Ashish Negi

unread,
Sep 24, 2013, 7:57:47 AM9/24/13
to nod...@googlegroups.com
I want to run some javascript from the node c++ addon. 

How can i call / push_task nodejs to execute my javascript so that everything of nodejs is available to the javascript_that_i_want_to_execute ?

Are there any existing example ?

I started learning libuv and found uv_aync_init and uv_async_send. But how are i integrated then with v8 main loop ?

I found node_contextify.h/cc but that is not made public for addons ?

Ben Noordhuis

unread,
Sep 24, 2013, 11:38:54 AM9/24/13
to nod...@googlegroups.com
There is no V8 main loop. There is however the libuv / node.js event loop.

If you want to execute JS code, you can do so with v8::Script::New()
or v8::Script::Compile(). If you want to call into an _existing_ JS
function, use either v8::Function::Call() or, preferably,
node::MakeCallback().

If your add-on uses threads, be advised that it's not safe to call
into V8 from another thread, not even when you have the Locker for the
main Isolate. You need to set up some kind of communication channel
with the main thread. uv_async_send() can help - it wakes up the
event loop - but you still need cross-thread synchronization, like a
mutex or an rwlock. Libuv provides mutexes, barriers, rwlocks, etc.
See uv.h for details.

Ashish Negi

unread,
Sep 25, 2013, 1:25:57 AM9/25/13
to nod...@googlegroups.com

> There is no V8 main loop.  There is however the libuv / node.js event loop.

Thanks for clarifying. I also meant that when nodejs does v8::Initialize() that thread becomes the v8 main thread.
 
If you want to execute JS code, you can do so with v8::Script::New()
or v8::Script::Compile().  If you want to call into an _existing_ JS
function, use either v8::Function::Call() or, preferably,
node::MakeCallback().

 
If your add-on uses threads, be advised that it's not safe to call
into V8 from another thread, not even when you have the Locker for the
main Isolate.  

Yes my addon has threads. I would heed  to your advise.

You need to set up some kind of communication channel
with the main thread.  uv_async_send() can help - it wakes up the
event loop - but you still need cross-thread synchronization, like a
mutex or an rwlock.  Libuv provides mutexes, barriers, rwlocks, etc.
See uv.h for details.

Does it mean that i would have to set up a uv_async_init with my watcher in node.cc code  ?

Ashish Negi

unread,
Sep 25, 2013, 3:04:33 AM9/25/13
to nod...@googlegroups.com
I found that doing 

       uv_async_init(uv_default_loop, &my_watcher);

would call the callbacks with my_watcher on node thread.
Is that right ?

Ben Noordhuis

unread,
Sep 25, 2013, 4:11:22 AM9/25/13
to nod...@googlegroups.com
Provided you call uv_async_send() afterwards from the other thread,
yes, that's correct.

Ashish Negi

unread,
Sep 25, 2013, 4:27:55 AM9/25/13
to nod...@googlegroups.com
Yaa that is working and i am able to execute simple js code => This definately implies that i am getting into v8 or node main thread. But i am not being able to execute node specific code like 

var fs = require('fs');

I guess since I am creating a new persistent context for myself, it would be independent of node's context. 
like:

        v8::HandleScope handleScope;
v8::Persistent<v8::Context> context = CreateShellContext();
context->Enter();
// unique_ptr makes sure that data would be deleted even if there is an exception.
std::unique_ptr<char[]> codeData((char*)handle->data);
RunCode(context, (char*)handle->data);
context->Exit();
context.Dispose();

How can i get the context of node or is there some method to execute the code in node's context.

Ben Noordhuis

unread,
Sep 25, 2013, 4:46:42 AM9/25/13
to nod...@googlegroups.com
You can get the current context with v8::Context::GetCurrent(). Enter
it, then call node::MakeCallback(). That function requires a `this`
object and a function but you can always create an anonymous function
with v8::Script::New(), then call it with v8::Context::Global() as the
`this` object. Brief example:

Local<Context> context = Context::GetCurrent();
Context::Scope context_scope(context);
const char source[] = "(function(x) { return x * x; })";
Local<String> script_source = String::New(source, sizeof(source) - 1);
Local<Script> script = Script::New(script_source);
Local<Value> value = script->Run();
assert(value.IsEmpty() == false);
assert(value->IsFunction() == true);
Local<Function> fn = value.As<Function>();
Local<Value> arg = Integer::New(42);
Local<Value> rc = MakeCallback(context->Global(), fn, 1, &arg);

Ashish Negi

unread,
Sep 25, 2013, 5:30:27 AM9/25/13
to nod...@googlegroups.com
I am just trying to run a JS code in a function. for now i think that i do not need MakeCallback.

I send in handle->data this js code:
var fs = require('fs');
fs.readFile('C:\\abc\\hello.txt', function(err,data){
if(err)
console.log("Error reading file: " + err);
else
console.log("Data is: " + data);
});

This is function that i gets the call:

void RunJavascriptUv(uv_async_t *handle, int status) {
// hope that this is being called on nodejs main loop.
v8::HandleScope handleScope;
// unique_ptr makes sure that data would be deleted even if there is an exception.
std::unique_ptr<char[]> codeData((char*)handle->data);
v8::Local<v8::Context> currContext = v8::Context::GetCurrent();
v8::Local<v8::String> source = v8::String::New(codeData.get());
v8::TryCatch try_catch;
v8::Local<v8::Script> script = v8::Script::Compile(source);
if (script.IsEmpty()) {
// Print errors that happened during compilation.
ReportException(NULL, &try_catch);
} else {
v8::Local<v8::Value> result = script->Run();
if (result.IsEmpty()) {
assert(try_catch.HasCaught());
// Print errors that happened during execution.
ReportException(NULL, &try_catch);
} else {
assert(!try_catch.HasCaught());
if (!result->IsUndefined()) {
// If all went well and the result wasn't undefined then print
// the returned value.
v8::String::Utf8Value str(result);
const char* cstr = ToCString(str);
printf("%s\n", cstr);
}
}
}
}

And i am getting this error:

undefined:1: ReferenceError: require is not defined
var fs = require('fs');
                        fs.readFile('C:\CEF\hello.txt', function(err
         ^
ReferenceError: require is not defined
    at <anonymous>:1:10

Ben Noordhuis

unread,
Sep 25, 2013, 5:50:14 AM9/25/13
to nod...@googlegroups.com
That's because require() is not a global function, see lib/module.js
for details.

Ashish Negi

unread,
Sep 25, 2013, 6:48:26 AM9/25/13
to nod...@googlegroups.com
Thanks for all the help, But what should i look for in module.js.

I thought NativeModule is the key, i tried with NativeModule.require but even that failed.

can i call something in javascript that would give me this function 'require' ?
Or 
How do i get into that context that gives me the umberalla of functions that are available at the time node try to execute a js file ?

Ashish Negi

unread,
Sep 25, 2013, 7:30:37 AM9/25/13
to nod...@googlegroups.com
I tried your method of MakeCallback : your code with my jscode in between the anonymous function :
Local<Context> context = Context::GetCurrent(); 
  Context::Scope context_scope(context); 
 std::string source = "(function(x) { " + std::string(codeData.get()) + " return x; });"; 
  Local<String> script_source = String::New(source.c_str(), sizeof(source) - 1); 
 Local<Script> script = Script::New(script_source);
 Local<Script> compiledScript = Script::Compile(script_source);
 if(script.IsEmpty()) {
 return ;   <--------- Execution goes here with Error on Nodejs console : JavaScript error (Uncaught SyntaxError: Unexpected end of input)
 }
  assert(value.IsEmpty() == false); 
  assert(value->IsFunction() == true); 
  Local<Function> fn = value.As<Function>(); 
  Local<Value> arg = Integer::New(42); 
  Local<Value> rc = MakeCallback(context->Global(), fn, 1, &arg); 


when the source is + "(function(x) { var fs = require('fs'); return x; });"

When i put it in a separate node process it shows [Function]. which means the syntax is right.

Any tips ?

Ashish Negi

unread,
Sep 25, 2013, 7:53:25 AM9/25/13
to nod...@googlegroups.com
The error is in sizeof(source)-1. It should be source.length() for my changes. 

std::string source = "(function(x) { " + std::string(codeData.get()) + " return x; });"; 
  Local<String> script_source = String::New(source.c_str(), sizeof(source) - 1); 


But still i can not use nodejs require function :( 

Jorge Chamorro

unread,
Sep 25, 2013, 9:43:53 AM9/25/13
to nod...@googlegroups.com
Put this in your main .js module, *before* the require() of your native .node module:

(function(){return this})().require= require;

Untested...
--
( Jorge )();

Ashish Negi

unread,
Sep 26, 2013, 1:36:09 AM9/26/13
to nod...@googlegroups.com
@Jorge Thanks but this also does not work the way i did it.
The final code that i gave the node to execute  "from c++ addon" is :

           (function(){return this})().require = require; var fs = require('fs');

and it gave me error 
          <unknown>:38: Uncaught ReferenceError: require is not defined.

When i put it like (function(x){  (function(){return this})().require = require; var fs = require('fs'); return x; })
        and executed this anonymous function node just crashed.

Is this i should have done it ?

Ashish Negi

unread,
Sep 26, 2013, 2:52:14 AM9/26/13
to nod...@googlegroups.com
I find that when i execute only node then it gets the 'require' and 'module'.
And when i run it with a test_file.js like "node test_file.js" and print the global in it, the global does not have the 'require'.

But then how does the very next line that uses 'require' does not throw the error ?

the contents of test_file.js is 

console.log(global);
var fs = require('fs');
console.log("\n\nAFTER********************************************************\n\n");
console.log(global);

and output is :

{ ArrayBuffer: [Function: ArrayBuffer],
  // ------ A lot of things -------------
  global: [Circular],
  process: 
   { title: 'C:\\Windows\\system32\\cmd.exe - node  c:\\CEF\\readFile.js ',
     version: 'v0.10.18',
  /// ------ A lot of things -------------
  Buffer: 
   { [Function: Buffer]
     isEncoding: [Function],
     poolSize: 8192,
     isBuffer: [Function: isBuffer],
     byteLength: [Function],
     concat: [Function] },
  setTimeout: [Function],
  setInterval: [Function],
  clearTimeout: [Function],
  clearInterval: [Function],
  setImmediate: [Function],
  clearImmediate: [Function],
  console: [Getter] }


AFTER********************************************************


{ ArrayBuffer: [Function: ArrayBuffer],
  // ------ A lot of things -------------
  global: [Circular],
  process: 
   { title: 'C:\\Windows\\system32\\cmd.exe - node  c:\\CEF\\readFile.js ',
     version: 'v0.10.18',
  /// ------ A lot of things -------------
  Buffer: 
   { [Function: Buffer]
     isEncoding: [Function],
     poolSize: 8192,
     isBuffer: [Function: isBuffer],
     byteLength: [Function],
     concat: [Function] },
  setTimeout: [Function],
  setInterval: [Function],
  clearTimeout: [Function],
  clearInterval: [Function],
  setImmediate: [Function],
  clearImmediate: [Function],
  console: [Getter] }


And without any file like running node.exe printing console shows :

> { ArrayBuffer: [Function: ArrayBuffer],
// -------- A lot of things here ------------
  Buffer: 
   { [Function: Buffer]
     isEncoding: [Function],
     poolSize: 8192,
     isBuffer: [Function: isBuffer],
     byteLength: [Function],
     concat: [Function],
     _charsWritten: 2 },
  setTimeout: [Function],
  setInterval: [Function],
  clearTimeout: [Function],
  clearInterval: [Function],
  setImmediate: [Function],
  clearImmediate: [Function],
  console: [Getter],
  module: 
   { id: 'repl',
     exports: 
      { writer: [Object],
        _builtinLibs: [Object],
        REPLServer: [Object],
        start: [Function],
        repl: [Object] },
     parent: undefined,
     filename: 'C:\\Users\\asnegi\\repl',
     loaded: false,
     children: [],
     paths: 
      [ 'C:\\Users\\asnegi\\repl\\node_modules',
        'C:\\Users\\asnegi\\node_modules',
        'C:\\Users\\node_modules',
        'C:\\node_modules' ] },
  require: 
   { [Function: require]
     resolve: [Function],
     main: undefined,
     extensions: { '.js': [Function], '.json': [Function], '.node': [Function] },
     registerExtension: [Function],
     cache: {} } }


Ashish Negi

unread,
Sep 26, 2013, 3:43:50 AM9/26/13
to nod...@googlegroups.com
@Ben Thank you. It took me a while to understand what did you meant "require() is not in global function".

@Jorge Thank you too. After reading your post it hit me that i should do global.request = request;

That just solves the problem that Ben has said. And it is working now. 

Last question though, Is there any side-effects of this ?

Jorge Chamorro

unread,
Sep 26, 2013, 10:49:30 AM9/26/13
to nod...@googlegroups.com
Nope :-)

Write this main.js module:

```
(function(){return this})().require = require;
var yourModule = require(/* the path to your native/C module */);
```

The idea is to clone `require` into a global before your module loads, so that your module has it handy (as a global).

Totally untested... but please let me know if it works.

Cheers,
--
( Jorge )();

Jorge Chamorro

unread,
Sep 26, 2013, 10:58:34 AM9/26/13
to nod...@googlegroups.com
On 26/09/2013, at 08:52, Ashish Negi wrote:

> I find that when i execute only node then it gets the 'require' and 'module'.
> And when i run it with a test_file.js like "node test_file.js" and print the global in it, the global does not have the 'require'.
>
> But then how does the very next line that uses 'require' does not throw the error ?

Because `require` is a parameter. Your test_file.js is called wrapped in a function:

```
(function (require, path, etc) {

/* the text of "test_file.js" goes here */

})(require, path, etc)
```

That's why it's in scope, but not because it's a global, it isn't.
--
( Jorge )();

Jorge Chamorro

unread,
Sep 26, 2013, 11:06:11 AM9/26/13
to nod...@googlegroups.com
On 26/09/2013, at 09:43, Ashish Negi wrote:

> @Ben Thank you. It took me a while to understand what did you meant "require() is not in global function".
>
> @Jorge Thank you too. After reading your post it hit me that i should do global.request = request;
>
> That just solves the problem that Ben has said. And it is working now.

Oh, well... then forget my previous 2 messages :-)

> Last question though, Is there any side-effects of this ?

Yes:

1.- Globals considered harmful.

If what your module needs is a reference to `require`, there are other ways to do it, without polluting the global scope.

[] Want to know more

:-)
--
( Jorge )().

KOS KOS

unread,
Nov 19, 2013, 4:09:35 AM11/19/13
to nod...@googlegroups.com, thisismy...@gmail.com
Please show the full code sample that works for you.

Четвер, 26 вересня 2013 р. 10:43:50 UTC+3 користувач Ashish Negi написав:

Jack lu

unread,
Mar 25, 2014, 11:25:20 PM3/25/14
to nod...@googlegroups.com, thisismy...@gmail.com


td1 = td_startTD2([]()->void{

try
{
while (1)
{

uv_work_t *req = new uv_work_t();
req->data = NULL;

int status = uv_queue_work(
uv_default_loop(),
req,
[](uv_work_t* req)->void{

},
[](uv_work_t* req, int status)->void{

HandleScope scope;

wstring jsCode = L"io.cw('123')";
v8::Local<v8::Script> script = v8::Script::Compile(toV8String(jsCode));
v8::Handle<v8::Value> result = script->Run();

});

td_sleep(10*1000);

}
}
catch (boost::thread_interrupted&)
{

io_cw("td1 exit ok.");

}

});
Reply all
Reply to author
Forward
0 new messages