Going Native

A friendly guide to native extensions for node.js

Jen Andre (jandre@gmail.com)

About

Hi, I'm Jen.

Full Stack Developer (but mostly backend)
Co-founder, CTO @ www.threatstack.com

What

Let's build a native module for node.js from the ground up

Why?

Performance

...But Think Carefully

Access to Native System & Library APIs

e.g.: sqlite, zeromq, messagepack

Problem

looking up uid for username

uid-process/index.js

var exec = require('child_process').exec;

exports.uid = function(username, cb) {

  exec('id -u ' + username, function(err, stdout, stderr) {
    cb(err, stdout);
  });

}
$  node 
> var m = require('./uid-process');  
undefined
>  m.uid('root', function(err,uid) {
... if (err) return console.error("got error:", err);
... console.log("uid:", uid);
... });  
undefined 
> uid: 0  

...any problems with this?

What happens if you run this 1000 times?

$ cat manytimes.js 
var m = require('./uid-process');

var counter = process.argv[2] || 1000;

console.log("getting uid", counter, "times");

for (var i = 0; i < counter; i++) {
    m.uid('root', function(err,uid) {
          if (err) return console.error("got error:", err);
     });
};
$  node manytimes.js 
getting uid times: 1000

child_process.js:927
    throw errnoException(process._errno, 'spawn');
          ^
Error: spawn EAGAIN
    at errnoException (child_process.js:980:11)
    at ChildProcess.spawn (child_process.js:927:11)
    ...
:(

a better way

$  man getpwnam 

NAME
     getpwent, getpwnam, getpwnam_r, getpwuid, getpwuid_r, getpwuuid, getpwuuid_r, setpassent, setpwent, endpwent -- password database operations

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <sys/types.h>
     #include <pwd.h>
     #include <uuid/uuid.h>

     struct passwd * getpwnam(const char *login);


"The getpwnam() function returns a pointer to a structure containing the broken-out fields of the record in the password database (e.g., the local password file /etc/passwd, NIS, and LDAP) that matches the username name."

struct passwd

   The passwd structure is defined in  as follows:
   
   struct passwd {
       char   *pw_name;       /* username */
       char   *pw_passwd;     /* user password */
       uid_t   pw_uid;        /* user ID */     <- we want this! 
       gid_t   pw_gid;        /* group ID */
       char   *pw_gecos;      /* user information */
       char   *pw_dir;        /* home directory */
       char   *pw_shell;      /* shell program */
   };
   

Goal

let's build a binding for getpwname :)

/**
* Get the uid for username.
*/

var pwname = require('./pwname');

var username = process.argv[2] || 'root';

try {
  console.log("Uid for", username, "is", pwname.uid(username));
} catch (err) {
  console.error("User does not exist:", username, err);
}

Getting Started

You'll need

  • node-gyp
    $ npm install node-gyp -g
  • gcc and other build tools for your environment
    • if you can build node.js, you are probably good to go: https://github.com/joyent/node/wiki/Installation

basic module structure

  • pwname/
    • binding.gyp
    • lib/
      • pwname.js
    • package.json
    • src/
      • pwname.cc

binding.gyp

how we build things

{
  "targets": [{
    "target_name": "pwname", 
    "sources": [ "src/pwname.cc" ]
  }]
}

lib/pwname.js

our javascript wrapper

var path = require('path');
var _pwname = require(path.join(__dirname, '../build/Release/pwname.node'));

/**
 * Get uid for user.
 *
 * @param {String} username - username
 *
 * @return {Integer} user id of user
 *
 */
exports.uid = function(username) {
  return _pwname.uid(username);
};
  • Our built node module in build/Release

src/pwname.cc

the c++ code

Now for the good stuff

src/pwname.cc

the basic structure

#include <v8.h> #include <node.h> #include <sys/types.h> #include <unistd.h> #include <pwd.h> using namespace node; using namespace v8; Handle<Value> Uid(const Arguments& args);
void Init(Handle<Object> target) { // ... }
Handle<Value> Uid(const Arguments& args) { // ... }
extern "C" { static void init (Handle<Object> target) {
Init(target);
}
NODE_MODULE(pwname, init);
}

void Init(Handle<Object> target)

initializing the module

void Init(Handle<Object> target) {
   HandleScope scope;
   target->Set(String::NewSymbol("uid"), 
      FunctionTemplate::New(Uid)->GetFunction());
}
  • target corresponds to module.exports in node.
  • exports.uid = Handle<Value> Uid(const Arguments& args);

Our uid() function

using namespace v8;

Handle<Value> Uid(const Arguments& args)
{
  HandleScope scope;
  struct passwd *user = NULL;
  
if (args.Length() > 0 && args[0]->IsString()) { v8::String::Utf8Value name(args[0]->ToString()); user = getpwnam(*name); } else { return ThrowException(Exception::Error( String::New("you must supply the username"))); }
if (user) { Handle<Value> result = v8::Number::New(user->pw_uid); return scope.Close(result); } else { return ThrowException(Exception::Error(String::New("username not found"))); } }
  • Always declare HandleScope at the top of your functions when you are creating v8 objects.
  • scope.Close(Handle) tells HandleScope not to free the memory associated with result.
  • On success, convert the uid value to a v8::Number.
  • All inputs/outputs that interact with javascript must be v8 datatypes live on the v8 heap.
    • v8::Array, v8::Object, v8::Function, v8::Number, etc
    • http://izs.me/v8-docs/classv8_1_1Object.html

More about Handles

"Smart Pointers"
Slice 1 Created with Sketch (http://www.bohemiancoding.com/sketch) v8:Handle<T> v8:Local<T> v8:Persistent<T>
Local<T> and Handle<T> contents freed when out of scope
void UselessFunction() {
 HandleScope scope;
 Local<Value> helloHandle = String::New("hello"); // allocation here
} // scope ends.  The string pointed to by helloHandle is auto-deleted.
Persistent<T> handles must be manually disposed.
Handle<Value> UselessFunction() {
 HandleScope scope;
 Persistent<Value> helloHandle = Persistent<String>::New(String::New("hello")); // allocation here 
 return helloHandle;
}  // v8::String will live until handle.Dispose() is called

Example: Returning Objects

Handle<Value> Get(const Arguments& args)
{
  HandleScope scope;
  ...

  if (user) {
    Local<Object> obj = Object::New();
    obj->Set(String::New("uid"), Number::New(user->pw_uid));
    obj->Set(String::New("info"), String::New(user->pw_gecos));
    obj->Set(String::New("dir"), String::New(user->pw_dir));
    obj->Set(String::New("shell"), String::New(user->pw_shell));
    obj->Set(String::New("gid"), Number::New(user->pw_gid));
    return scope.Close(obj);
  } else {
    return ThrowException(Exception::Error(String::New("username not found")));
  }
}
$  node 
> require('./pwname/build/Release/pwname.node').get('root');  
{ uid: 0,
info: 'System Administrator',
dir: '/var/root',
shell: '/bin/sh',
gid: 0 }
  

Example: Calling JS Functions

Handle<Value> Get(const Arguments& args)  // module.get(username, callback)
{
  HandleScope scope;
  struct passwd *user = NULL;
  Local<Function> callback;

  if (args.Length() > 1 && args[1]->IsFunction()) {
    callback = Local<Function>::Cast(args[1]);
  }

  [...] 

  if (user) {
    Local<Object> obj = Object::New();
    obj->Set(String::New("uid"), Number::New(user->pw_uid));
    [...] 

    if (callback.IsEmpty()) {
      return scope.Close(obj);
    } else { 
      Local<Value> argv[] = { Local<Value>::New(v8::Null()), obj };
      // callback(null, obj); 
      return callback->Call(Context::GetCurrent()->Global(), 2, argv); 
    }
  } else {
    [...]
  }
}

Survived so far?

next

wrapping c++ objects for javascript

async work using libuv

Your Mission

a simple Time class

var Time = require('./simpletime').Time;
var time = new Time();
console.log("Epoch is", time.epoch()); // Epoch is 1371486877
console.log("Time is", time.string()); // Time is Mon Jun 17 12:34:37 2013
time.addSeconds(10);
console.log("Epoch is", time.epoch()); // Epoch is 1371486887
console.log("Time is", time.string()); // Time is Mon Jun 17 12:34:47 2013

Our tools

#include <stdio.h>
#include <time.h>

int main ()
{
  time_t rawtime;
  struct tm * timeinfo;
  char timestring[26];

  time (&rawtime);  /* get the current time and store in rawtime */
  timeinfo = localtime (&rawtime); /* convert time -> timeinfo */
  strftime(timestring, 26, "%c", timeinfo);
  printf ("Current local time and date: %s\n", timestring); 

  return 0;
}

simpletime.cc definition

class Time : public node::ObjectWrap {

  public:
    static Handle<Value> New(const Arguments &args); // new Time();
    static Handle<Value> Epoch(const Arguments &args); // time.epoch();
    static Handle<Value> String(const Arguments &args); // time.string();
    static Handle<Value> AddSeconds(const Arguments &args); // time.addSeconds(60);

  private:
    Time();  // constructor
    ~Time(); // destructor
    time_t value;
    int getEpoch();
    void addSeconds(int);
    char * getString();

};

void Init(Handle<Object> target);

 // ... implementations elided 

extern "C" {
  static void init(Handle<Object> target)
  {
    Init(target);
  }

  NODE_MODULE(simpletime, init);
}

Init()

void Init(Handle<Object> target) {
  HandleScope scope;
  
Local<FunctionTemplate> tpl = FunctionTemplate::New(Time::New); tpl->SetClassName(String::NewSymbol("Time")); tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->PrototypeTemplate()->Set(String::NewSymbol("epoch"), FunctionTemplate::New(Time::Epoch)->GetFunction()); tpl->PrototypeTemplate()->Set(String::NewSymbol("string"), FunctionTemplate::New(Time::String)->GetFunction()); tpl->PrototypeTemplate()->Set(String::NewSymbol("addSeconds"), FunctionTemplate::New(Time::AddSeconds)->GetFunction());
Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction()); target->Set(String::NewSymbol("Time"), constructor);
}
  • function Time() {};
  • Time.prototype.constructor = ...
  • exports.Time = Time;
  • Time.prototype.epoch = function() ...
  • Time.prototype.string = function() ...
  • Time.prototype.addSeconds = function() ...

ObjectWrap->Wrap()

creating our Time object


Handle<Value> Time::New(const Arguments& args) {
  HandleScope scope;
  Time* obj = new Time();
  obj->Wrap(args.This());
  return args.This();
}

Calling a method

Handle<Value> Time::AddSeconds(const Arguments& args)
{
  HandleScope scope;
  Time* obj = Time::Unwrap<Time>(args.This());
  if (args.Length() > 0 && args[0]->IsNumber()) {
    int seconds = args[0]->ToInteger()->Value();
    obj->addSeconds(seconds);
  }
  return scope.Close(v8::Undefined());
}

Async Example

Async Fibonacci?

"Here's a fun fact: every function call that does CPU work also blocks. This function, which calculates the n'th Fibonacci number, will block the current thread of execution because it's using the CPU."
-- `node js is cancer` by Ted Dzuiba
function fibonacci(n) {

  if (n <= 0)
    return 0;

  if (n == 1)
    return 1;

  return fibonacci(n  - 1) + fibonacci(n - 2);
}

Yes, it's possible

var fibonacci = require('build/Release/fibonacci.node');

var n = 20;

fibonacci.calculate(n, function(err, result) {
  console.log("Fibonacci for n", n, "is", result);
});

console.log("Hey! This executes right away, cuz fibonacci is async! ..."); 

uv_queue_work to the rescue

uv_queue_work(uv_default_loop(), &context->request,
      FibonacciWorker, FibonacciWorkerAfter);
  • Runs FibonacciWorker function in a thread from libuv's worker pool
  • On completion, FibonacciWorkerAfter executed in event loop.
  • context->request is an uv_work_tobject that stores our input/output values, and gets supplied to both functions.

Context for uv_queue_work worker functions

struct FibonacciContext {

  uv_work_t request;
  int input; // the value passed to fibonacci();
  unsigned int result; // save the result to be calculated
  Persistent<Function> callback; // js callback

  FibonacciContext(int n, Handle<Function> callback) {
    this->input = n;
    this->callback = Persistent<Function>::New(callback);
    // set the data to point to myself
    this->request.data = this;
  }

  ~FibonacciContext() {
    this->request.data = NULL;
    this->callback.Dispose();
  }
};
request->data == FibonacciContext

fibonacci.calculate()

Handle<Value> Calculate(const Arguments& args) {
  HandleScope scope;
  Local<Function> callback;
  int n;
  if (args.Length() > 0 && args[0]->IsNumber()) {
    n = args[0]->ToInteger()->Value();
  } else {
    return ThrowException(Exception::Error(
      String::New("You must supply an integer")));
  }

  if (args.Length() > 1 && args[1]->IsFunction()) {
    callback = Local<Function>::Cast(args[1]);
  } else {
    return ThrowException(Exception::Error(
      String::New("You must supply a callback")));
  }
  FibonacciContext *context = 
    new FibonacciContext(n, callback);

  uv_queue_work(uv_default_loop(), &context->request,
         FibonacciWorker, FibonacciWorkerAfter);

  return scope.Close(Undefined());
}
void FibonacciWorker(uv_work_t *req) {
  FibonacciContext* context = 
      static_cast<FibonacciContext*>(req->data);
  int n = context->input;
  context->result = fibonacci(n);
}

Do not access v8 memory in the worker!!!

void FibonacciWorkerAfter(uv_work_t *req) {
  HandleScope scope;
  FibonacciContext* context =
    static_cast<FibonacciContext*>(req->data);
  Local<Value> argv[] =
    { Null(), Number::New(context->result) };
  // call the callback(null, result);
  context->callback->Call(
    Context::GetCurrent()->Global(), 2, argv);
  delete context;
  return;
};
  • fibonacci.calculate(n, function(err, result) { ... })
  • Result from our calculation
  • callback(err, result)

Tips

ThankYou

Slides + Code: https://github.com/jandre/node-native-presentation