Reputation: 1335
I am trying to build an application using Node.Js which requires the native module support. I have used libuv library through the application and I was able to make most of the async methods work except for the part where I had to implement the progress event callback. I want to implement the progress event callback asynchronously and without blocking the Node.js event loop.
Here are code snippets:
native.cc
#include <node.h>
#include <uv.h>
#include "nbind/nbind.h"
using v8::Isolate;
using v8::HandleScope;
int FileProgressCallback(uint64_t const sent, uint64_t const total, void const *const data) {
nbind::cbFunction cb = *((nbind::cbFunction *) data);
cb(sent, total);
return 0;
}
class WorkerFileTransfer {
public:
WorkerFileTransfer(std::string path, nbind::cbFunction cb) :
callback(cb), path(path) {};
uv_work_t worker;
nbind::cbFunction callback;
bool error;
std::string errorMsg;
std::string path;
};
void FileTransferDone(uv_work_t *order, int status) {
Isolate *isolate = Isolate::GetCurrent();
HandleScope handleScope(isolate);
WorkerFileTransfer *work = static_cast< WorkerFileTransfer * >( order->data );
if (work->error) {
work->callback.call<void>(work->errorMsg.c_str(), work->output);
} else {
ThirdPartyLibraryFileCopy(work->path.c_str(), FileProgressCallback, (const void *) &work->callback);
}
// Memory cleanup
work->callback.reset();
delete work;
}
void FileTransferRunner(uv_work_t *order) {
WorkerFileTransfer *work = static_cast< WorkerFileTransfer * >( order->data );
try {
work->output = true;
}
catch (...) {
work->error = true;
work->errorMsg = "Error occured while executing the method...";
}
}
void FileTransfer(const std::string path, nbind::cbFunction &callback) {
WorkerFileTransfer *work = new WorkerFileTransfer(path, callback);
work->worker.data = work;
work->path = path;
work->error = false;
uv_queue_work(uv_default_loop(), &work->worker, FileTransferRunner, FileTransferDone);
}
function(FileTransfer);
test.js
FileTransfer(
'/path/file.txt',
(sent, total) => {
console.log(`sent`, sent);
console.log('total', total);
}
);
I was able to achieve the file transfer done because of the below lines but the Node.Js event loop gets blocked here.
void FileTransferDone(uv_work_t *order, int status) {
...
ThirdPartyLibraryFileCopy(work->path.c_str(), FileProgressCallback, (const void *) &work->callback);
...
}
When I move the line ThirdPartyLibraryFileCopy(work->path.c_str(), FileProgressCallback, (const void *) &work->callback);
into the FileTransferRunner(uv_work_t *order)
method then I am not getting any output in the javascript callback function.
How do I get the progress values in the javascript callback function, asynchronously, without blocking the Node.Js event loop?
Upvotes: 1
Views: 603
Reputation: 2010
As per the uv_queue_work
documentation: http://docs.libuv.org/en/v1.x/threadpool.html#c.uv_queue_work The work callback (FileTransferRunner
in your case) runs on the threadpool but the done callback (FileTransferDone
in your case) runs on the loop thread. Thus, if you perform a blocking operation on the latter, you are going to block the loop.
If you want to send periodic progress reports this model won't work well for you. You could use an async handle uv_async_t
and report progress using uv_async_send
(which is thread-safe) from the work function. Or use multiple work requests to send chuncks (that will probably be slower).
Upvotes: 2