Skip to content

Commit

Permalink
Porting asynchronous example from NAN repo
Browse files Browse the repository at this point in the history
The original example is hosted here:
https://github.com/nodejs/nan/tree/master/examples/async_pi_estimate
Rewrite all the logic using **node-addon-api**
See the following issue
https://github.com/nodejs/abi-stable-node-addon-examples/issues/10
  • Loading branch information
NickNaso committed Mar 2, 2018
1 parent 09885f5 commit 6ee6492
Show file tree
Hide file tree
Showing 22 changed files with 477 additions and 0 deletions.
1 change: 1 addition & 0 deletions async_pi_estimate/nan/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In this directory run `node-gyp rebuild` and then `node ./addon.js`
23 changes: 23 additions & 0 deletions async_pi_estimate/nan/addon.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <nan.h>
#include "sync.h" // NOLINT(build/include)
#include "async.h" // NOLINT(build/include)

using v8::FunctionTemplate;
using v8::Handle;
using v8::Object;
using v8::String;
using Nan::GetFunction;
using Nan::New;
using Nan::Set;

// Expose synchronous and asynchronous access to our
// Estimate() function
NAN_MODULE_INIT(InitAll) {
Set(target, New<String>("calculateSync").ToLocalChecked(),
GetFunction(New<FunctionTemplate>(CalculateSync)).ToLocalChecked());

Set(target, New<String>("calculateAsync").ToLocalChecked(),
GetFunction(New<FunctionTemplate>(CalculateAsync)).ToLocalChecked());
}

NODE_MODULE(addon, InitAll)
44 changes: 44 additions & 0 deletions async_pi_estimate/nan/addon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
var addon = require('./build/Release/addon');
var calculations = process.argv[2] || 100000000;

function printResult(type, pi, ms) {
console.log(type, 'method:');
console.log('\tπ ≈ ' + pi +
' (' + Math.abs(pi - Math.PI) + ' away from actual)');
console.log('\tTook ' + ms + 'ms');
console.log();
}

function runSync() {
var start = Date.now();
// Estimate() will execute in the current thread,
// the next line won't return until it is finished
var result = addon.calculateSync(calculations);
printResult('Sync', result, Date.now() - start);
}

function runAsync() {
// how many batches should we split the work in to?
var batches = process.argv[3] || 16;
var ended = 0;
var total = 0;
var start = Date.now();

function done (err, result) {
total += result;

// have all the batches finished executing?
if (++ended === batches) {
printResult('Async', total / batches, Date.now() - start);
}
}

// for each batch of work, request an async Estimate() for
// a portion of the total number of calculations
for (var i = 0; i < batches; i++) {
addon.calculateAsync(calculations / batches, done);
}
}

runSync();
runAsync();
56 changes: 56 additions & 0 deletions async_pi_estimate/nan/async.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <nan.h>
#include "pi_est.h" // NOLINT(build/include)
#include "async.h" // NOLINT(build/include)

using v8::Function;
using v8::Local;
using v8::Number;
using v8::Value;
using Nan::AsyncQueueWorker;
using Nan::AsyncWorker;
using Nan::Callback;
using Nan::HandleScope;
using Nan::New;
using Nan::Null;
using Nan::To;

class PiWorker : public AsyncWorker {
public:
PiWorker(Callback *callback, int points)
: AsyncWorker(callback), points(points), estimate(0) {}
~PiWorker() {}

// Executed inside the worker-thread.
// It is not safe to access V8, or V8 data structures
// here, so everything we need for input and output
// should go on `this`.
void Execute () {
estimate = Estimate(points);
}

// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use V8 again
void HandleOKCallback () {
HandleScope scope;

Local<Value> argv[] = {
Null()
, New<Number>(estimate)
};

callback->Call(2, argv, async_resource);
}

private:
int points;
double estimate;
};

// Asynchronous access to the `Estimate()` function
NAN_METHOD(CalculateAsync) {
int points = To<int>(info[0]).FromJust();
Callback *callback = new Callback(To<Function>(info[1]).ToLocalChecked());

AsyncQueueWorker(new PiWorker(callback, points));
}
8 changes: 8 additions & 0 deletions async_pi_estimate/nan/async.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
#define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_

#include <nan.h>

NAN_METHOD(CalculateAsync);

#endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
14 changes: 14 additions & 0 deletions async_pi_estimate/nan/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"targets": [
{
"target_name": "addon",
"sources": [
"addon.cc",
"pi_est.cc",
"sync.cc",
"async.cc"
],
"include_dirs": ["<!(node -e \"require('nan')\")"]
}
]
}
11 changes: 11 additions & 0 deletions async_pi_estimate/nan/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "async_work",
"version": "0.0.0",
"description": "Node.js Addons Example #9",
"main": "addon.js",
"private": true,
"gypfile": true,
"dependencies": {
"nan": "*"
}
}
55 changes: 55 additions & 0 deletions async_pi_estimate/nan/pi_est.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <cstdlib>
#include "pi_est.h" // NOLINT(build/include)

/*
Estimate the value of π by using a Monte Carlo method.
Take `points` samples of random x and y values on a
[0,1][0,1] plane. Calculating the length of the diagonal
tells us whether the point lies inside, or outside a
quarter circle running from 0,1 to 1,0. The ratio of the
number of points inside to outside gives us an
approximation of π/4.
See https://en.wikipedia.org/wiki/File:Pi_30K.gif
for a visualization of how this works.
*/

inline int randall(unsigned int *p_seed) {
// windows has thread safe rand()
#ifdef _WIN32
return rand(); // NOLINT(runtime/threadsafe_fn)
#else
return rand_r(p_seed);
#endif
}

double Estimate (int points) {
int i = points;
int inside = 0;
unsigned int randseed = 1;

#ifdef _WIN32
srand(randseed);
#endif

// unique seed for each run, for threaded use
unsigned int seed = randall(&randseed);

#ifdef _WIN32
srand(seed);
#endif

while (i-- > 0) {
double x = randall(&seed) / static_cast<double>(RAND_MAX);
double y = randall(&seed) / static_cast<double>(RAND_MAX);

// x & y and now values between 0 and 1
// now do a pythagorean diagonal calculation
// `1` represents our 1/4 circle
if ((x * x) + (y * y) <= 1)
inside++;
}

// calculate ratio and multiply by 4 for π
return (inside / static_cast<double>(points)) * 4;
}
6 changes: 6 additions & 0 deletions async_pi_estimate/nan/pi_est.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_
#define EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_

double Estimate(int points);

#endif // EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_
12 changes: 12 additions & 0 deletions async_pi_estimate/nan/sync.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <nan.h>
#include "pi_est.h" // NOLINT(build/include)
#include "sync.h" // NOLINT(build/include)

// Simple synchronous access to the `Estimate()` function
NAN_METHOD(CalculateSync) {
// expect a number as the first argument
int points = info[0]->Uint32Value();
double est = Estimate(points);

info.GetReturnValue().Set(est);
}
8 changes: 8 additions & 0 deletions async_pi_estimate/nan/sync.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_
#define EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_

#include <nan.h>

NAN_METHOD(CalculateSync);

#endif // EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_
1 change: 1 addition & 0 deletions async_pi_estimate/node-addon-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In this directory run `node-gyp rebuild` and then `node ./addon.js`
13 changes: 13 additions & 0 deletions async_pi_estimate/node-addon-api/addon.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <napi.h>
#include "sync.h" // NOLINT(build/include)
#include "async.h" // NOLINT(build/include)

// Expose synchronous and asynchronous access to our
// Estimate() function
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "calculateSync"), Napi::Function::New(env, CalculateSync));
exports.Set(Napi::String::New(env, "calculateAsync"), Napi::Function::New(env, CalculateAsync));
return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
44 changes: 44 additions & 0 deletions async_pi_estimate/node-addon-api/addon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
var addon = require('bindings')('addon');
var calculations = process.argv[2] || 100000000;

function printResult(type, pi, ms) {
console.log(type, 'method:');
console.log('\tπ ≈ ' + pi +
' (' + Math.abs(pi - Math.PI) + ' away from actual)');
console.log('\tTook ' + ms + 'ms');
console.log();
}

function runSync() {
var start = Date.now();
// Estimate() will execute in the current thread,
// the next line won't return until it is finished
var result = addon.calculateSync(calculations);
printResult('Sync', result, Date.now() - start);
}

function runAsync() {
// how many batches should we split the work in to?
var batches = process.argv[3] || 16;
var ended = 0;
var total = 0;
var start = Date.now();

function done (err, result) {
total += result;

// have all the batches finished executing?
if (++ended === batches) {
printResult('Async', total / batches, Date.now() - start);
}
}

// for each batch of work, request an async Estimate() for
// a portion of the total number of calculations
for (var i = 0; i < batches; i++) {
addon.calculateAsync(calculations / batches, done);
}
}

runSync();
runAsync();
39 changes: 39 additions & 0 deletions async_pi_estimate/node-addon-api/async.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include <napi.h>
#include "pi_est.h" // NOLINT(build/include)
#include "async.h" // NOLINT(build/include)

class PiWorker : public Napi::AsyncWorker {
public:
PiWorker(Napi::Function& callback, int points)
: Napi::AsyncWorker(callback), points(points), estimate(0) {}
~PiWorker() {}

// Executed inside the worker-thread.
// It is not safe to access V8, or V8 data structures
// here, so everything we need for input and output
// should go on `this`.
void Execute () {
estimate = Estimate(points);
}

// Executed when the async work is complete
// this function will be run inside the main event loop
// so it is safe to use V8 again
void OnOK() {
Napi::HandleScope scope(Env());
Callback().Call({Env().Undefined(), Napi::Number::New(Env(), estimate)});
}

private:
int points;
double estimate;
};

// Asynchronous access to the `Estimate()` function
Napi::Value CalculateAsync(const Napi::CallbackInfo& info) {
int points = info[0].As<Napi::Number>().Uint32Value();
Napi::Function callback = info[1].As<Napi::Function>();
PiWorker* piWorker = new PiWorker(callback, points);
piWorker->Queue();
return info.Env().Undefined();
}
8 changes: 8 additions & 0 deletions async_pi_estimate/node-addon-api/async.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
#define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_

#include <napi.h>

Napi::Value CalculateAsync(const Napi::CallbackInfo& info);

#endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_
33 changes: 33 additions & 0 deletions async_pi_estimate/node-addon-api/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"targets": [
{
"target_name": "addon",
"sources": [
"addon.cc",
"pi_est.cc",
"sync.cc",
"async.cc"
],
'cflags!': [ '-fno-exceptions' ],
'cflags_cc!': [ '-fno-exceptions' ],
'include_dirs': ["<!@(node -p \"require('node-addon-api').include\")"],
'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],
'conditions': [
['OS=="win"', {
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1
}
}
}],
['OS=="mac"', {
"xcode_settings": {
"CLANG_CXX_LIBRARY": "libc++",
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
'MACOSX_DEPLOYMENT_TARGET': '10.7'
}
}]
]
}
]
}
Loading

0 comments on commit 6ee6492

Please sign in to comment.