Skip to content

Commit 4372037

Browse files
author
John French
committed
src: implement AsyncProgressQueueWorker
PR-URL: nodejs/node-addon-api#585 Fixes: nodejs/node-addon-api#582 Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
1 parent 78b89d9 commit 4372037

File tree

8 files changed

+595
-32
lines changed

8 files changed

+595
-32
lines changed

doc/async_progress_worker.md renamed to doc/async_worker_variants.md

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,12 @@ called and are executed as part of the event loop.
272272
The code below shows a basic example of the `Napi::AsyncProgressWorker` implementation:
273273
274274
```cpp
275-
#include<napi.h>
275+
#include <napi.h>
276276
277277
#include <chrono>
278278
#include <thread>
279279
280-
use namespace Napi;
280+
using namespace Napi;
281281
282282
class EchoWorker : public AsyncProgressWorker<uint32_t> {
283283
public:
@@ -323,7 +323,7 @@ The following code shows an example of how to create and use an `Napi::AsyncProg
323323
// Include EchoWorker class
324324
// ..
325325

326-
use namespace Napi;
326+
using namespace Napi;
327327

328328
Value Echo(const CallbackInfo& info) {
329329
// We need to validate the arguments here
@@ -341,4 +341,116 @@ asynchronous task ends and other data needed for the computation. Once created,
341341
the only other action needed is to call the `Napi::AsyncProgressWorker::Queue`
342342
method that will queue the created worker for execution.
343343
344+
# AsyncProgressQueueWorker
345+
346+
`Napi::AsyncProgressQueueWorker` acts exactly like `Napi::AsyncProgressWorker`
347+
except that each progress committed by `Napi::AsyncProgressQueueWorker::ExecutionProgress::Send`
348+
during `Napi::AsyncProgressQueueWorker::Execute` is guaranteed to be
349+
processed by `Napi::AsyncProgressQueueWorker::OnProgress` on the JavaScript
350+
thread in the order it was committed.
351+
352+
For the most basic use, only the `Napi::AsyncProgressQueueWorker::Execute` and
353+
`Napi::AsyncProgressQueueWorker::OnProgress` method must be implemented in a subclass.
354+
355+
# AsyncProgressQueueWorker::ExecutionProcess
356+
357+
A bridge class created before the worker thread execution of `Napi::AsyncProgressQueueWorker::Execute`.
358+
359+
## Methods
360+
361+
### Send
362+
363+
`Napi::AsyncProgressQueueWorker::ExecutionProcess::Send` takes two arguments, a pointer
364+
to a generic type of data, and a `size_t` to indicate how many items the pointer is
365+
pointing to.
366+
367+
The data pointed to will be copied to internal slots of `Napi::AsyncProgressQueueWorker` so
368+
after the call to `Napi::AsyncProgressQueueWorker::ExecutionProcess::Send` the data can
369+
be safely released.
370+
371+
`Napi::AsyncProgressQueueWorker::ExecutionProcess::Send` guarantees invocation
372+
of `Napi::AsyncProgressQueueWorker::OnProgress`, which means multiple `Send`
373+
call will result in the in-order invocation of `Napi::AsyncProgressQueueWorker::OnProgress`
374+
with each data item.
375+
376+
```cpp
377+
void Napi::AsyncProgressQueueWorker::ExecutionProcess::Send(const T* data, size_t count) const;
378+
```
379+
380+
## Example
381+
382+
The code below shows a basic example of the `Napi::AsyncProgressQueueWorker` implementation:
383+
384+
```cpp
385+
#include <napi.h>
386+
387+
#include <chrono>
388+
#include <thread>
389+
390+
using namespace Napi;
391+
392+
class EchoWorker : public AsyncProgressQueueWorker<uint32_t> {
393+
public:
394+
EchoWorker(Function& callback, std::string& echo)
395+
: AsyncProgressQueueWorker(callback), echo(echo) {}
396+
397+
~EchoWorker() {}
398+
// This code will be executed on the worker thread
399+
void Execute(const ExecutionProgress& progress) {
400+
// Need to simulate cpu heavy task
401+
for (uint32_t i = 0; i < 100; ++i) {
402+
progress.Send(&i, 1)
403+
std::this_thread::sleep_for(std::chrono::seconds(1));
404+
}
405+
}
406+
407+
void OnOK() {
408+
HandleScope scope(Env());
409+
Callback().Call({Env().Null(), String::New(Env(), echo)});
410+
}
411+
412+
void OnProgress(const uint32_t* data, size_t /* count */) {
413+
HandleScope scope(Env());
414+
Callback().Call({Env().Null(), Env().Null(), Number::New(Env(), *data)});
415+
}
416+
417+
private:
418+
std::string echo;
419+
};
420+
```
421+
422+
The `EchoWorker`'s constructor calls the base class' constructor to pass in the
423+
callback that the `Napi::AsyncProgressQueueWorker` base class will store
424+
persistently. When the work on the `Napi::AsyncProgressQueueWorker::Execute`
425+
method is done the `Napi::AsyncProgressQueueWorker::OnOk` method is called and
426+
the results are returned back to JavaScript when the stored callback is invoked
427+
with its associated environment.
428+
429+
The following code shows an example of how to create and use an
430+
`Napi::AsyncProgressQueueWorker`.
431+
432+
```cpp
433+
#include <napi.h>
434+
435+
// Include EchoWorker class
436+
// ..
437+
438+
using namespace Napi;
439+
440+
Value Echo(const CallbackInfo& info) {
441+
// We need to validate the arguments here.
442+
Function cb = info[1].As<Function>();
443+
std::string in = info[0].As<String>();
444+
EchoWorker* wk = new EchoWorker(cb, in);
445+
wk->Queue();
446+
return info.Env().Undefined();
447+
}
448+
```
449+
450+
The implementation of a `Napi::AsyncProgressQueueWorker` can be used by creating a
451+
new instance and passing to its constructor the callback to execute when the
452+
asynchronous task ends and other data needed for the computation. Once created,
453+
the only other action needed is to call the `Napi::AsyncProgressQueueWorker::Queue`
454+
method that will queue the created worker for execution.
455+
344456
[`Napi::AsyncWorker`]: ./async_worker.md

0 commit comments

Comments
 (0)