|
| 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 | +} |
0 commit comments