Skip to content

Commit

Permalink
src: implement AsyncProgressQueueWorker
Browse files Browse the repository at this point in the history
PR-URL: nodejs/node-addon-api#585
Fixes: nodejs/node-addon-api#582
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Gabriel Schulhof <[email protected]>
  • Loading branch information
wroy7860 committed Mar 18, 2020
1 parent 578f8a3 commit 6da4aed
Show file tree
Hide file tree
Showing 8 changed files with 595 additions and 32 deletions.
118 changes: 115 additions & 3 deletions doc/async_progress_worker.md → doc/async_worker_variants.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,12 @@ called and are executed as part of the event loop.
The code below shows a basic example of the `Napi::AsyncProgressWorker` implementation:
```cpp
#include<napi.h>
#include <napi.h>
#include <chrono>
#include <thread>
use namespace Napi;
using namespace Napi;
class EchoWorker : public AsyncProgressWorker<uint32_t> {
public:
Expand Down Expand Up @@ -323,7 +323,7 @@ The following code shows an example of how to create and use an `Napi::AsyncProg
// Include EchoWorker class
// ..

use namespace Napi;
using namespace Napi;

Value Echo(const CallbackInfo& info) {
// We need to validate the arguments here
Expand All @@ -341,4 +341,116 @@ asynchronous task ends and other data needed for the computation. Once created,
the only other action needed is to call the `Napi::AsyncProgressWorker::Queue`
method that will queue the created worker for execution.
# AsyncProgressQueueWorker
`Napi::AsyncProgressQueueWorker` acts exactly like `Napi::AsyncProgressWorker`
except that each progress committed by `Napi::AsyncProgressQueueWorker::ExecutionProgress::Send`
during `Napi::AsyncProgressQueueWorker::Execute` is guaranteed to be
processed by `Napi::AsyncProgressQueueWorker::OnProgress` on the JavaScript
thread in the order it was committed.
For the most basic use, only the `Napi::AsyncProgressQueueWorker::Execute` and
`Napi::AsyncProgressQueueWorker::OnProgress` method must be implemented in a subclass.
# AsyncProgressQueueWorker::ExecutionProcess
A bridge class created before the worker thread execution of `Napi::AsyncProgressQueueWorker::Execute`.
## Methods
### Send
`Napi::AsyncProgressQueueWorker::ExecutionProcess::Send` takes two arguments, a pointer
to a generic type of data, and a `size_t` to indicate how many items the pointer is
pointing to.
The data pointed to will be copied to internal slots of `Napi::AsyncProgressQueueWorker` so
after the call to `Napi::AsyncProgressQueueWorker::ExecutionProcess::Send` the data can
be safely released.
`Napi::AsyncProgressQueueWorker::ExecutionProcess::Send` guarantees invocation
of `Napi::AsyncProgressQueueWorker::OnProgress`, which means multiple `Send`
call will result in the in-order invocation of `Napi::AsyncProgressQueueWorker::OnProgress`
with each data item.
```cpp
void Napi::AsyncProgressQueueWorker::ExecutionProcess::Send(const T* data, size_t count) const;
```

## Example

The code below shows a basic example of the `Napi::AsyncProgressQueueWorker` implementation:

```cpp
#include <napi.h>

#include <chrono>
#include <thread>

using namespace Napi;

class EchoWorker : public AsyncProgressQueueWorker<uint32_t> {
public:
EchoWorker(Function& callback, std::string& echo)
: AsyncProgressQueueWorker(callback), echo(echo) {}

~EchoWorker() {}
// This code will be executed on the worker thread
void Execute(const ExecutionProgress& progress) {
// Need to simulate cpu heavy task
for (uint32_t i = 0; i < 100; ++i) {
progress.Send(&i, 1)
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

void OnOK() {
HandleScope scope(Env());
Callback().Call({Env().Null(), String::New(Env(), echo)});
}

void OnProgress(const uint32_t* data, size_t /* count */) {
HandleScope scope(Env());
Callback().Call({Env().Null(), Env().Null(), Number::New(Env(), *data)});
}

private:
std::string echo;
};
```
The `EchoWorker`'s constructor calls the base class' constructor to pass in the
callback that the `Napi::AsyncProgressQueueWorker` base class will store
persistently. When the work on the `Napi::AsyncProgressQueueWorker::Execute`
method is done the `Napi::AsyncProgressQueueWorker::OnOk` method is called and
the results are returned back to JavaScript when the stored callback is invoked
with its associated environment.
The following code shows an example of how to create and use an
`Napi::AsyncProgressQueueWorker`.
```cpp
#include <napi.h>
// Include EchoWorker class
// ..
using namespace Napi;
Value Echo(const CallbackInfo& info) {
// We need to validate the arguments here.
Function cb = info[1].As<Function>();
std::string in = info[0].As<String>();
EchoWorker* wk = new EchoWorker(cb, in);
wk->Queue();
return info.Env().Undefined();
}
```

The implementation of a `Napi::AsyncProgressQueueWorker` can be used by creating a
new instance and passing to its constructor the callback to execute when the
asynchronous task ends and other data needed for the computation. Once created,
the only other action needed is to call the `Napi::AsyncProgressQueueWorker::Queue`
method that will queue the created worker for execution.

[`Napi::AsyncWorker`]: ./async_worker.md
Loading

0 comments on commit 6da4aed

Please sign in to comment.