Tag Archives: C/C++ Addon

Node.js C/C++ module is actually simple

Node.js is one of most unexpected technology for me JavaScript on server side was unbelievable thing for me 5 years ago. And to be honest JavaScript was not very straight forward technology with main goal to handle HTML/CSS based UI. But now we have several successful Node.js projects in Tesseris Pro and is seems like everything will be done with JavaScript soon. And JavaScript itself become much more serious and straight forward language. In my last post I described possible ways to run asynchronous operation in Electron.

Another problem related to that electron project was problem of creation of C/C++ module for Image Magic library. There was several modules in npm some of them was just CLI wrappers some of them was wrappers on C++ API. Both of them seems to be a wrappers created by somebody to solve their exact problem and do not solve my problems in addition CLI wrappers are slow. Thus I decided to create one more limited wrapper just for my needs – imagemagik2 hope one day I will be able to make it more or les full featured. But let me describe my experience with C/C++ Node.JS module creation…

You can find source code here: https://github.com/TesserisPro/imagmagick2

What C/C++ Node.JS modules are?

Node.JS native module is a DLL (or equivalent for other OS) file that contains some code that interacts with Node.js through V8 API. This file is renamed to *.node by specific build procedure. You will be able to manipulate JavaScript abstraction representation – crete variables, functions, object control their parameters, etc. inside your C/C++ module.

Bad news:
– V8 API is extremely over-complicated and you should be experienced C/C++ developer to use it.
– Node.js introduce additional abstraction layer Native Abstractions for Node.js (NAN) to simplify module programming that has very poor documentation, but you have to use it because without that abstraction your module may be not compatible with old or new versions of Node.js
– You have to recompile your module for every exact version of Node.js and for every platform of course. If you will try to use module compiled for another version of node even if it will have only minor changes and you are on the same platform you will see error during loading this module like “Module version mismatch. Expected 48, got 47”.

Interesting news:
– Module building tool (node-gyp) enforces you to build cross-platform module
– You will need python to build your extension. It is not a problem actually but it’s funny 🙂

Good news:
– It is not so complex as you thought first 🙂

Hello world module

You can find simple module sample in Node.js documentation.

Module startup

Seems to be obsolete

On Node.js web page you can find that entry point of your module is void Init(Local exports, Local module) function and module registration should be done with NODE_MODULE(addon_name, Init). To add module content you can just register all parts of your module by adding them to exports parameter. Another option is to overwrite whole exports by function or anything you want. Exactly the same idea as in usual JavaScript modules module.exports = {...}.

The type Local is actually object reference managed by the v8 garbage collector. According to V8 API documentation there are two types of handles: local and persistent handles. Local handles are light-weight and transient and typically used in local operations. They are managed by HandleScopes. Persistent handles can be used when storing objects across several independent operations and have to be explicitly deallocated when they’re no longer used.

Seems to be actual

According to NAN documentation entry point of are NAN macros NAN_MODULE_INIT(init){ } and NODE_MODULE(your_module_name, init) where init is just an identifier and can be changed.

To add something to your exports you can use NAN macro NAN_EXPORT(target, your_object) the most confusing part here is target you never define it. It is just naming convention defined in nan.h and node.h

// nan.h
#define NAN_MODULE_INIT(name) void name(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target)

// node.h
#define NODE_MODULE(modname, regfunc) NODE_MODULE_X(modname, regfunc, NULL, 0)

The NAN is full of macros that defines hidden variable and other C language objects. That makes it very hard to understand.

Full code of module startup with registration of method looks like following

#include "std.h"

NAN_METHOD(my_method) {
    // Write code here;    
}

NAN_MODULE_INIT(init) {
    NAN_EXPORT(target, my_method);
}

NODE_MODULE(my_module, init);

Creating a function

To add some functionality to our module you can create a function and register it. And here we have hidden identified again. Here is marco from nan.h

<br />#define NAN_METHOD(name)  Nan::NAN_METHOD_RETURN_TYPE name(Nan::NAN_METHOD_ARGS_TYPE info)

You can use info argument to read function arguments in following way

    double myNumberValue = info[0].As<v8::Number>()->Value(); // First argument
    Nan::Utf8String myStringValue(info[1].As<v8::String>()); // Second argument
    char *actualString = *myStringValue; //Way to access string

As you can see here we have some NAN wrappers again, and this time Nan::Utf8String is useful thing it saves several lines of code related to V8 string implementation.

To send result of your calculations back to JavaScript world you can set return value by following code

// Create new V8 object
v8::Local<v8::Object> result = Nan::New<v8::Object>();

// Set some object fields
Nan::Set(result, v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), "my_field_name"), Nan::New((int)my_filed_value));

// Set object as return value
info.GetReturnValue().Set(result);

Here you can find v8::String::NewFromUtf8, unfortunately I did not found a way to create string with NAN, so I have to do it with V8 API. Also good point here is Nan::GetCurrentContext()-&gt;GetIsolate() that method returns object of type Isolate* that object is required for most V8 API calls and represent something like V8 managed heap – a space where all variables live and die with GC.

Using async workers

In most cases you want to create asynchronous functions to not block node.js main tread. You can use general C/C++ threads management but V8 and Node.js are not thread safe and if you call info.GetReturnValue().Set(result) from wrong thread you can damage data and you will get exception for sure.

NAN introduce Nan::AsyncWorker a class with several virtual methods that should be overridden to crate async operation and simplify dispatching results back from another thread. Most important are HandleOKCallback, HandleErrorCallback and Execute methods. Execute method is running in separate thread and perform asynchronous operation. HandleOKCallback is called in case Execute finished without problems. HandleErrorCallback will be called in case of error in Execute method. Thus to implement asynchronous operation with callback you can inherit Nan::AsyncWorker class and override virtual methods in following way.

class Worker : public Nan::AsyncWorker {
    public:
        Worker(Nan::Callback *callback) : AsyncWorker(callback) 
        {

        }

        void Execute() 
        {
            if (do_my_asyc_action() != MY_SUCCESS_VALUE) 
            {
                this->SetErrorMessage("Error!!!");
            }
        }
    protected:
        void HandleOKCallback()
        {
            v8::Local<v8::Value> argv[] = { 
                v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), "Some result string")
            };

            // Call callback function
            this->callback->Call(
                1,     // Number of arguments
                argv); // Array of arguments
        }

        void HandleErrorCallback()
        {
            v8::Local<v8::Value> argv[] = { 
                v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), this->ErrorMessage()) 
            };

            // Call callback function with error
            this->callback->Call(1, argv);
        }
};

Building your module

The build system configuration is more or less simple. You should create a JSON file named binding.gyp and add your source files and other options to this json file. The build will be always just compilation of your C/C++ files. The node-gyp will automatically prepare building configurations for every platform during module installation. On Windows it will create solution/project files and build your module with Visual Studio, on linux it will prepare makefile and build everything with gcc. Bellow you can find one of the simplest binding.gyp file.

{
  "targets": [{
                "target_name": 'my_module',
                "sources": [ "main.cpp" ],
             }]
}

Additionally you can configure specific options for specific platforms. You can find more about node-gyp here https://github.com/nodejs/node-gyp.

To build your module automatically during installation add following script section to package.json

"scripts": {
    "install": "node-gyp rebuild"
}

To build your module during development you can use node-gyp command with build/rebuild parameter.

Conclusion

The V8 and NAN API are complicated and have not very detailed documentation thus my idea is to keep C/C++ module as simple as possible and use only methods and async workers without creation of complex JavaScript structures through API. This will allow to avoid memory leaks (in some cases it is very hard to understand how and when you should free memory from docs) and other problems. You can add complicated JavaScript wrapper on your simplified async methods inside your module and create rich and easy to use API. I used this approach in my module here https://github.com/TesserisPro/imagmagick2

Electron. Asynchrony, Modules and C/C++

There are a lot of posts about NodeJS describing innovative solutions about JavaScript on server side. However there is another place where JavaScript, V8 and Chrome HTML engine can be applied. That place is your desktop and Electron is a technology that makes possible to create desktop applications with JavaScript/HTML. I’m sure most of us already have experience in Electron. I’m sure that everybody who is reading have at least one Electron based application on this desktop. And some of that applications are really awesome. For example Visual Studio Code, Slack or Atom the beginning of Electron platform. So I’m a little late with this post, but let me share some of my problems and way to solve them.

What is Electron?

According to documentation Electron is NodeJS and Chromium tunning on the same V8 engine. As result we have something like possibility to run node modules in browser. That will allow to access local file system or use any C/C++ modules.
Any Electron application has at least two processes, one main process and one or more renderer. The main process is pure NodeJS process and entry point of the application. Main process is responsible of creation of windows and every window has it’s own renderer process.

In renderer process you are inside Chrome browser but with power of NodeJS.

You can easy communicate between processes with IPC. Electron gives you very simple API for this.

I think that is more that enough about Electron to have general picture of it. You can try it with this tutorial http://electron.atom.io/docs/tutorial/quick-start/.

Asynchronous operations

It is very hard to imagine modern desktop operation without asynchronous operations. We need to show progress while loading something, we need to perform background calculations and many many more features like these. However Electron is not so good in asynchronous operations.

One of the first idea to do something in async way is to send IPC message to main window and wait for results. You may not believe me if I will say that this may freeze your UI but consider following sample

https://github.com/peleshenko/freezing_node_sample

Clone it run npm install and execute run.sh.

When we are trying to freeze UI with long running loop you can not type in input field but css animation is working and you can close window. But when we are trying to freeze application with long running loop in main process even css animation is freezed and you can not close the window.

Amazing!

You can review corresponding discussion on Electron page on github https://github.com/electron/electron/issues/3363

Short answer:

... a blocking operation in main process is easy to block all other processes... ...it is required to put IO and CPU-bound operations in new threads to avoid this...

So the first lesson learn – forget about main process, live it alone, this process is for Chromium not for you, use your own.

Next idea is to use WebWorkers. Electron supports WebWorkers. But unfortunately in WebWorkers you have no access to NodeJS. You can use usual browser API, or calculate something but no NodeJS. That fact makes WebWorkers useless in Electron because most operations that requires async execution are related to file system or other external things.

So there are only one way start something in parallel thread using JavaScript is child_process.fork. Not very simple but working way.

Alternatively you can create a C/C++ addons and do anything you want inside this addon with C++, start any number of threads and dispatch results to Electron’s message loop with callback. But that is another story…

C/C++ Addons

As you can see addons is not something unusual even simple async operation may require addon creation. Additional reason that may enforce you to implement your own addon with C/C++ is quality of existing addons in npm. Some of them are really terrible.

After my last word I think you recall your last attempt of npm module installation in Windows. You had to install specific version of VisualStudio and Python 2.7. My first reaction was “What? The module author is crazy he is going to mix C++, Python and NodeJS. Give me another module…”. But all modules that has C/C++ code inside will require Python and VisualStudio and some times specific version of VisualStudio.

You need all this software to install module because of NodeJS build system node-gyp. That is the only way to include C/C++ addon to your npm module. That is strange thing but if you will take a look a little deeper you will understand the reason. But let me leave this topic for another post.

OK finally you installed VisualStudio and Python and installed module. Starting your Electron application and see following in dev tools console (numbers may differ):

Module version mismatch. Expected 48, got 47"

The source of that problem is that node addon is only compatible with exactly the same node version that this addon has been built for. And you just have built is for your NodeJS but Electron as a little different NodeJS version inside. Doh!!!

To solve the problem you can use electron-rebuild. Follow the instruction and you will be able to rebuild required module for NodeJS version used in Electron.

And the last…

That is it about Electron and problems with it in general. We have more and more projects with Electron in Tesseris Pro. So I hope to share my experience in C/C++ addons and React in Electron in next posts.

And once more. Electron is Chromium + NodeJS, thus it already has NodeJS module system and that system is not so bad. And as for me it looks very strange when people tries to use system.js or require.js or any other JavaScript module system to load modules in Electron. Electron is not a browser! It is NodeJS + browser! Use it as NodeJS!