Skip to content

Commit 4cfadb2

Browse files
committed
split up and document
1 parent 4b461ad commit 4cfadb2

File tree

9 files changed

+165
-81
lines changed

9 files changed

+165
-81
lines changed

9_async_work/addon.cc

Lines changed: 4 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,11 @@
11
#include <node.h>
2-
#include <cstdlib>
2+
#include "sync.h"
3+
#include "async.h"
34

45
using namespace v8;
56

6-
double Estimate (int points) {
7-
int i = points;
8-
int inside = 0;
9-
unsigned int seed = rand();
10-
double r1, r2;
11-
12-
while (i-- > 0) {
13-
r1 = rand_r(&seed) / (double)RAND_MAX;
14-
r2 = rand_r(&seed) / (double)RAND_MAX;
15-
16-
if ((r1 * r1) + (r2 * r2) <= 1)
17-
inside++;
18-
}
19-
20-
21-
return (inside / (double)points) * 4;
22-
}
23-
24-
Handle<Value> CalculateSync(const Arguments& args) {
25-
Isolate* isolate = Isolate::GetCurrent();
26-
HandleScope scope(isolate);
27-
28-
int points = args[0]->Uint32Value();
29-
double est = Estimate(points);
30-
31-
return scope.Close(Number::New(est));
32-
}
33-
34-
typedef struct AsyncData {
35-
int points;
36-
Persistent<Function> callback;
37-
double estimate;
38-
} AsyncData;
39-
40-
void AsyncWork(uv_work_t *req) {
41-
AsyncData *asyncData = (AsyncData *)req->data;
42-
asyncData->estimate = Estimate(asyncData->points);
43-
}
44-
45-
void AsyncAfter(uv_work_t *req) {
46-
Isolate* isolate = Isolate::GetCurrent();
47-
HandleScope scope(isolate);
48-
49-
AsyncData *asyncData = (AsyncData *)req->data;
50-
Handle<Value> argv[] = {
51-
Null(),
52-
Number::New(isolate, asyncData->estimate)
53-
};
54-
55-
TryCatch try_catch;
56-
asyncData->callback->Call(Context::GetCurrent()->Global(), 2, argv);
57-
asyncData->callback.Dispose(isolate);
58-
delete asyncData;
59-
delete req;
60-
if (try_catch.HasCaught())
61-
node::FatalException(try_catch);
62-
}
63-
64-
Handle<Value> CalculateAsync(const Arguments& args) {
65-
Isolate* isolate = Isolate::GetCurrent();
66-
HandleScope scope(isolate);
67-
68-
uv_work_t *req = new uv_work_t;
69-
AsyncData *asyncData = new AsyncData;
70-
req->data = asyncData;
71-
asyncData->points = args[0]->Uint32Value();
72-
Local<Function> callback = Local<Function>::Cast(args[1]);
73-
asyncData->callback = Persistent<Function>::New(isolate, callback);
74-
75-
uv_queue_work(
76-
uv_default_loop()
77-
, req
78-
, AsyncWork
79-
, (uv_after_work_cb)AsyncAfter
80-
);
81-
82-
return scope.Close(Undefined());
83-
}
84-
7+
// Expose synchronous and asynchronous access to our
8+
// Estimate() function
859
void InitAll(Handle<Object> exports) {
8610
exports->Set(String::NewSymbol("calculateSync"),
8711
FunctionTemplate::New(CalculateSync)->GetFunction());

9_async_work/addon.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ function printResult(type, pi, ms) {
1111

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

1820
function runAsync () {
21+
// how many batches should we split the work in to?
1922
var batches = process.argv[3] || 16;
2023
var ended = 0;
2124
var total = 0;
@@ -24,11 +27,14 @@ function runAsync () {
2427
function done (err, result) {
2528
total += result;
2629

30+
// have all the batches finished executing?
2731
if (++ended == batches) {
2832
printResult('Async', total / batches, Date.now() - start)
2933
}
3034
}
3135

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

9_async_work/async.cc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include <node.h>
2+
#include "pi_est.h"
3+
#include "async.h"
4+
5+
using namespace v8;
6+
7+
// libuv allows us to pass around a pointer to an arbitrary
8+
// object when running asynchronous functions. We create a
9+
// data structure to hold the data we need during and after
10+
// the async work.
11+
typedef struct AsyncData {
12+
int points; // estimation points
13+
Persistent<Function> callback; // callback function
14+
double estimate; // estimation result
15+
} AsyncData;
16+
17+
// Function to execute inside the worker-thread.
18+
// It is not safe to access V8, or V8 data structures
19+
// here, so everything we need for input and output
20+
// should go on our req->data object.
21+
void AsyncWork(uv_work_t *req) {
22+
// fetch our data structure
23+
AsyncData *asyncData = (AsyncData *)req->data;
24+
// run Estimate() and assign the result to our data structure
25+
asyncData->estimate = Estimate(asyncData->points);
26+
}
27+
28+
// Function to execute when the async work is complete
29+
// this function will be run inside the main event loop
30+
// so it is safe to use V8 again
31+
void AsyncAfter(uv_work_t *req) {
32+
Isolate* isolate = Isolate::GetCurrent();
33+
HandleScope scope(isolate);
34+
35+
// fetch our data structure
36+
AsyncData *asyncData = (AsyncData *)req->data;
37+
// create an arguments array for the callback
38+
Handle<Value> argv[] = {
39+
Null(),
40+
Number::New(isolate, asyncData->estimate)
41+
};
42+
43+
// surround in a try/catch for safety
44+
TryCatch try_catch;
45+
// execute the callback function
46+
asyncData->callback->Call(Context::GetCurrent()->Global(), 2, argv);
47+
if (try_catch.HasCaught())
48+
node::FatalException(try_catch);
49+
50+
// dispose the Persistent handle so the callback
51+
// function can be garbage-collected
52+
asyncData->callback.Dispose(isolate);
53+
// clean up any memory we allocated
54+
delete asyncData;
55+
delete req;
56+
}
57+
58+
// Asynchronous access to the `Estimate()` function
59+
Handle<Value> CalculateAsync(const Arguments& args) {
60+
Isolate* isolate = Isolate::GetCurrent();
61+
HandleScope scope(isolate);
62+
63+
// create an async work token
64+
uv_work_t *req = new uv_work_t;
65+
// assign our data structure that will be passed around
66+
AsyncData *asyncData = new AsyncData;
67+
req->data = asyncData;
68+
69+
// expect a number as the first argument
70+
asyncData->points = args[0]->Uint32Value();
71+
// expect a function as the second argument
72+
// we create a Persistent reference to it so
73+
// it won't be garbage-collected
74+
asyncData->callback = Persistent<Function>::New(isolate,
75+
Local<Function>::Cast(args[1]));
76+
77+
// pass the work token to libuv to be run when a
78+
// worker-thread is available to
79+
uv_queue_work(
80+
uv_default_loop(),
81+
req, // work token
82+
AsyncWork, // work function
83+
(uv_after_work_cb)AsyncAfter // function to run when complete
84+
);
85+
86+
return scope.Close(Undefined());
87+
}

9_async_work/async.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <node.h>
2+
3+
v8::Handle<v8::Value> CalculateAsync(const v8::Arguments& args);

9_async_work/binding.gyp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
"targets": [
33
{
44
"target_name": "addon",
5-
"sources": [ "addon.cc" ]
5+
"sources": [
6+
"addon.cc",
7+
"pi_est.cc",
8+
"sync.cc",
9+
"async.cc"
10+
]
611
}
712
]
813
}

9_async_work/pi_est.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <cstdlib>
2+
#include "pi_est.h"
3+
4+
/*
5+
Estimate the value of π by using a Monte Carlo method.
6+
Take `points` samples of random x and y values on a
7+
[0,1][0,1] plane. Calculating the length of the diagonal
8+
tells us whether the point lies inside, or outside a
9+
quarter circle running from 0,1 to 1,0. The ratio of the
10+
number of points inside to outside gives us an
11+
approximation of π/4.
12+
13+
See https://en.wikipedia.org/wiki/File:Pi_30K.gif
14+
for a visualization of how this works.
15+
*/
16+
17+
double Estimate (int points) {
18+
int i = points;
19+
int inside = 0;
20+
// unique seed for each run, for threaded use
21+
unsigned int seed = rand();
22+
double x, y;
23+
24+
while (i-- > 0) {
25+
// rand_r() is used to avoid thread locking
26+
x = rand_r(&seed) / (double)RAND_MAX;
27+
y = rand_r(&seed) / (double)RAND_MAX;
28+
29+
// x & y and now values between 0 and 1
30+
// now do a pythagorean diagonal calculation
31+
// `1` represents our 1/4 circle
32+
if ((x * x) + (y * y) <= 1)
33+
inside++;
34+
}
35+
36+
// calculate ratio and multiply by 4 for π
37+
return (inside / (double)points) * 4;
38+
}

9_async_work/pi_est.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
double Estimate (int points);

9_async_work/sync.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <node.h>
2+
#include "pi_est.h"
3+
#include "sync.h"
4+
5+
using namespace v8;
6+
7+
// Simple synchronous access to the `Estimate()` function
8+
Handle<Value> CalculateSync(const Arguments& args) {
9+
Isolate* isolate = Isolate::GetCurrent();
10+
HandleScope scope(isolate);
11+
12+
// expect a number as the first argument
13+
int points = args[0]->Uint32Value();
14+
double est = Estimate(points);
15+
16+
return scope.Close(Number::New(est));
17+
}

9_async_work/sync.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <node.h>
2+
3+
v8::Handle<v8::Value> CalculateSync(const v8::Arguments& args);

0 commit comments

Comments
 (0)