-
Notifications
You must be signed in to change notification settings - Fork 125
Expand file tree
/
Copy pathmodulename.cpp
More file actions
137 lines (112 loc) · 5.09 KB
/
modulename.cpp
File metadata and controls
137 lines (112 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <node.h>
#include <string>
using namespace v8;
// Forward declaration. Usually, you do this in a header file.
Handle<Value> Async(const Arguments& args);
void AsyncWork(uv_work_t* req);
void AsyncAfter(uv_work_t* req);
// We use a struct to store information about the asynchronous "work request".
struct Baton {
// This handle holds the callback function we'll call after the work request
// has been completed in a threadpool thread. It's persistent so that V8
// doesn't garbage collect it away while our request waits to be processed.
// This means that we'll have to dispose of it later ourselves.
Persistent<Function> callback;
// Tracking errors that happened in the worker function. You can use any
// variables you want. E.g. in some cases, it might be useful to report
// an error number.
bool error;
std::string error_message;
// Custom data you can pass through.
int32_t result;
};
// This is the function called directly from JavaScript land. It creates a
// work request object and schedules it for execution.
Handle<Value> Async(const Arguments& args) {
HandleScope scope;
if (!args[0]->IsFunction()) {
return ThrowException(Exception::TypeError(
String::New("First argument must be a callback function")));
}
// There's no ToFunction(), use a Cast instead.
Local<Function> callback = Local<Function>::Cast(args[0]);
// The baton holds our custom status information for this asynchronous call,
// like the callback function we want to call when returning to the main
// thread and the status information.
Baton* baton = new Baton();
baton->error = false;
baton->callback = Persistent<Function>::New(callback);
// This creates the work request struct.
uv_work_t *req = new uv_work_t();
req->data = baton;
// Schedule our work request with libuv. Here you can specify the functions
// that should be executed in the threadpool and back in the main thread
// after the threadpool function completed.
int status = uv_queue_work(uv_default_loop(), req, AsyncWork,
(uv_after_work_cb)AsyncAfter);
assert(status == 0);
return Undefined();
}
// This function is executed in another thread at some point after it has been
// scheduled. IT MUST NOT USE ANY V8 FUNCTIONALITY. Otherwise your extension
// will crash randomly and you'll have a lot of fun debugging.
// If you want to use parameters passed into the original call, you have to
// convert them to PODs or some other fancy method.
void AsyncWork(uv_work_t* req) {
Baton* baton = static_cast<Baton*>(req->data);
// Do work in threadpool here.
baton->result = 42;
// If the work we do fails, set baton->error_message to the error string
// and baton->error to true.
}
// This function is executed in the main V8/JavaScript thread. That means it's
// safe to use V8 functions again. Don't forget the HandleScope!
void AsyncAfter(uv_work_t* req) {
HandleScope scope;
Baton* baton = static_cast<Baton*>(req->data);
if (baton->error) {
Local<Value> err = Exception::Error(String::New(baton->error_message.c_str()));
// Prepare the parameters for the callback function.
const unsigned argc = 1;
Local<Value> argv[argc] = { err };
// Wrap the callback function call in a TryCatch so that we can call
// node's FatalException afterwards. This makes it possible to catch
// the exception from JavaScript land using the
// process.on('uncaughtException') event.
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
} else {
// In case the operation succeeded, convention is to pass null as the
// first argument before the result arguments.
// In case you produced more complex data, this is the place to convert
// your plain C++ data structures into JavaScript/V8 data structures.
const unsigned argc = 2;
Local<Value> argv[argc] = {
Local<Value>::New(Null()),
Local<Value>::New(Integer::New(baton->result))
};
// Wrap the callback function call in a TryCatch so that we can call
// node's FatalException afterwards. This makes it possible to catch
// the exception from JavaScript land using the
// process.on('uncaughtException') event.
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
// The callback is a permanent handle, so we have to dispose of it manually.
baton->callback.Dispose();
// We also created the baton and the work_req struct with new, so we have to
// manually delete both.
delete baton;
delete req;
}
void RegisterModule(Handle<Object> target) {
target->Set(String::NewSymbol("async"),
FunctionTemplate::New(Async)->GetFunction());
}
NODE_MODULE(modulename, RegisterModule);