Skip to content

networkimprov/lode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

##lode


####lode brings C/C++ libraries to Node.js applications

Copyright 2014 by Liam Breck

###Rationale

Achieving the potential of a high-level language like Javascript requires making the huge population of C & C++ libraries accessible within it. Currently the common approach to this for Node.js is to create a C++ module containing "glue code" that moves data between Javascript and library calls. Such a module must use the libuv and V8 apis.

The V8 api is arcane and poorly documented. Its use requires a lot of code. Its misuse causes subtle crash and memory-leak bugs. Implementing asynchronous calls on libuv requires before/during/after functions. Third-party glue modules may stop getting updates, or evolve in unexpected and unhelpful directions. These issues are a barrier to the possibilities of Node.js apps.

The lode toolkit lets C/C++ library users and developers focus on the library.

###Mechanism

We need JS access to a C library, "libxyz"

          | app.js
process A | libxyz.js api module: check args and form JSON messages (unique to libxyz)
          | lode.js module
          | node

    JSON messages via Domain Socket

          | lode shell
process B | libxyz_lode.so glue code: call library, package results (unique to libxyz)
          | libxyz.so|a

The lode shell app can handle many requests concurrently, and is designed to hand-off requests to glue code without a context switch beyond any incurred by use of the socket. This mechanism should be nearly as fast as asynchronous operations within the Node thread pool.

For applications that cannot bear the modest overhead of IPC, a version of the lode shell implemented as a native Node module is envisioned. Such a scheme would also permit synchronous invocation of the library api.

###Project Status

Dependencies: Node.js v0.10.x, Linux

The lode toolkit is currently a Linux-specific prototype.

Feedback and pull requests gratefully considered!

Todo:

  • support in-stream BLOBs following JSON data
  • connect to multiple Node instances
  • terminate idle threads after period of inactivity
  • performance test vs Node fs async functions

###Getting Started

1) Write a lode module for the library, or the parts of it which you need (demo/math_lode.cc)

#include <math.h>
#include <stdio.h>
#include <string.h>
#include "lode.h" // the lode api; it's very simple!

static char sReplyErr[] = "{'_id':'%s','error':%d}"; // response templates
static char sReplyCos[] = "{'_id':'%s','cos':%f}";
  // requires "_id" giving the value of _id from message
  // if has "_more", lode.js will retain the callback for that _id

const char* initialize(int argc, char* argv[]) {
  char* aReplies[] = { sReplyErr, sReplyCos, NULL };
  for (int i=0; aReplies[i]; ++i)
    flipQuote(aReplies[i]); // flip ' & " characters
  // initialize the library
  return NULL;
}

static const char* sQid[] = {"_id", NULL}; // JsonQuery inputs
static const char* sQop[] = {"op", NULL};
static const char* sQno[] = {"no", NULL};

void handleMessage(JsonValue* op, ThreadQ* q) {
  JsonQuery aQ(op);
  JsonValue* aId = aQ.select(sQid).next();
  JsonValue* aOp = aQ.select(sQop).next();
  char aTmp[2048];
  int aLen=0;
  if (aOp && aOp->isString()) {
    // call library
    if (!strcmp((char*)aOp->s.buf, "cosine")) {
      JsonValue* aNo = aQ.select(sQno).next();
      if (aNo && (aNo->isDouble() || aNo->isInt()))
        aLen = snprintf(aTmp, sizeof(aTmp), sReplyCos, aId->s.buf, cos(aNo->isInt() ? aNo->i * 1.0 : aNo->d));
    }
  }
  if (aLen <= 0 || aLen > (int)sizeof(aTmp)) {
    aLen = snprintf(aTmp, sizeof(aTmp), sReplyErr, aId->s.buf, aLen);
  }
  q->postMsg(aTmp, aLen);
}

2) Write a Node.js api for the library (demo/math.js)

var lode = require('../lode.js');
var sLib = __dirname+'/../math_lode.so';
var sEvents = {};

module.exports.on = function(event, callback) {
  if (typeof event !== 'string' || typeof callback !== 'function')
    throw new Error('arguments are: String event, Function callback');
  sEvents[event] = callback;
};

module.exports.init = function(params) {
  lode.load(sLib, params, function(op, data) {
    if (sEvents[op])
      return sEvents[op](data);
    if (op === 'error')
      throw data;
  });
};

module.exports.cosine = function(n, callback) {
  if (typeof n !== 'number' || typeof callback !== 'function')
    throw new Error('arguments are: Number n, Function callback');
  lode.call(sLib, {op:'cosine',no:n}, callback);
};

3) Write a Node.js app using the library api (demo/mathapp.js)

var math = require('./math.js');

math.on('connect', function() {
  console.log('math library ready');
  
  var aN = 42;
  math.cosine(aN, function cb(err, data, more) {
    if (err) throw err;
    console.log('cosine of '+aN+' is '+data.cos);
    if (!more)
      setTimeout(math.cosine, 2000, ++aN, cb);
  });
});

math.on('disconnect', function() {
  console.log('math library quit');
});

math.init(null);

4) Build and run

$ make
$ node demo/mathapp.js

###Core Components

lode.js - node.js module which spawns and talks to shell

src/shell.cc - library host program

src/yajl* - JSON parser, patched to allow pause/resume of parsing

src/json.h - defines JSON utility api

About

lode brings C/C++ libraries to Node.js applications

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •