Skip to content

Commit 1242c9a

Browse files
eljefedelrodeodeljefekkoopa
authored andcommitted
AsyncProgressWorker: add template types for .send
This allows for arbitrary types to be sent from .send. To achieve this, the abstract base class can be implemented with a template itself. Either at implementation or instantiation a type MUST be declared though.
1 parent c4cf44d commit 1242c9a

File tree

8 files changed

+186
-26
lines changed

8 files changed

+186
-26
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ LINT_SOURCES = \
3535
test/cpp/accessors2.cpp \
3636
test/cpp/asyncworker.cpp \
3737
test/cpp/asyncprogressworker.cpp \
38+
test/cpp/asyncprogressworkerstream.cpp \
3839
test/cpp/asyncprogressworkersignal.cpp \
3940
test/cpp/asyncworkererror.cpp \
4041
test/cpp/buffer.cpp \

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ NAN's `node::Buffer` helpers exist as the API has changed across supported Node
244244
`Nan::AsyncWorker` and `Nan::AsyncProgressWorker` are helper classes that make working with asynchronous code easier.
245245

246246
- <a href="doc/asyncworker.md#api_nan_async_worker"><b><code>Nan::AsyncWorker</code></b></a>
247-
- <a href="doc/asyncworker.md#api_nan_async_progress_worker"><b><code>Nan::AsyncProgressWorker</code></b></a>
247+
- <a href="doc/asyncworker.md#api_nan_async_progress_worker"><b><code>Nan::AsyncProgressWorkerBase & Nan::AsyncProgressWorker</code></b></a>
248248
- <a href="doc/asyncworker.md#api_nan_async_queue_worker"><b><code>Nan::AsyncQueueWorker</code></b></a>
249249

250250
### Strings & Bytes

doc/asyncworker.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,17 @@ class AsyncWorker {
5858
```
5959
6060
<a name="api_nan_async_progress_worker"></a>
61-
### Nan::AsyncProgressWorker
61+
### Nan::AsyncProgressWorkerBase & Nan::AsyncProgressWorker
6262
63-
`Nan::AsyncProgressWorker` is an _abstract_ class that extends `Nan::AsyncWorker` and adds additional progress reporting callbacks that can be used during the asynchronous work execution to provide progress data back to JavaScript.
63+
`Nan::AsyncProgressWorkerBase` is an _abstract_ class template that extends `Nan::AsyncWorker` and adds additional progress reporting callbacks that can be used during the asynchronous work execution to provide progress data back to JavaScript.
64+
65+
Previously the definiton of `Nan::AsyncProgressWorker` only allowed sending `const char` data. Now extending `Nan::AsyncProgressWorker` will yield an instance of the implicit `Nan::AsyncProgressWorkerBase` template with type `<char>` for compatibility.
6466
6567
Definition:
6668
6769
```c++
68-
class AsyncProgressWorker : public AsyncWorker {
70+
template<class T>
71+
class AsyncProgressWorkerBase<T> : public AsyncWorker {
6972
public:
7073
explicit AsyncProgressWorker(Callback *callback_);
7174
@@ -76,14 +79,16 @@ class AsyncProgressWorker : public AsyncWorker {
7679
class ExecutionProgress {
7780
public:
7881
void Signal() const;
79-
void Send(const char* data, size_t size) const;
82+
void Send(const T* data, size_t size) const;
8083
};
8184
8285
virtual void Execute(const ExecutionProgress& progress) = 0;
8386
84-
virtual void HandleProgressCallback(const char *data, size_t size) = 0;
87+
virtual void HandleProgressCallback(const T *data, size_t size) = 0;
8588
8689
virtual void Destroy();
90+
91+
typedef AsyncProgressWorkerBase<T> AsyncProgressWorker;
8792
```
8893

8994
<a name="api_nan_async_queue_worker"></a>

nan.h

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,9 +1578,11 @@ class Callback {
15781578
char *errmsg_;
15791579
};
15801580

1581-
/* abstract */ class AsyncProgressWorker : public AsyncWorker {
1581+
1582+
template<class T>
1583+
/* abstract */ class AsyncProgressWorkerBase : public AsyncWorker {
15821584
public:
1583-
explicit AsyncProgressWorker(Callback *callback_)
1585+
explicit AsyncProgressWorkerBase(Callback *callback_)
15841586
: AsyncWorker(callback_), asyncdata_(NULL), asyncsize_(0) {
15851587
async = new uv_async_t;
15861588
uv_async_init(
@@ -1593,45 +1595,45 @@ class Callback {
15931595
uv_mutex_init(&async_lock);
15941596
}
15951597

1596-
virtual ~AsyncProgressWorker() {
1598+
virtual ~AsyncProgressWorkerBase() {
15971599
uv_mutex_destroy(&async_lock);
15981600

15991601
delete[] asyncdata_;
16001602
}
16011603

16021604
void WorkProgress() {
16031605
uv_mutex_lock(&async_lock);
1604-
char *data = asyncdata_;
1606+
T *data = asyncdata_;
16051607
size_t size = asyncsize_;
16061608
asyncdata_ = NULL;
16071609
uv_mutex_unlock(&async_lock);
16081610

1609-
// Dont send progress events after we've already completed.
1611+
// Don't send progress events after we've already completed.
16101612
if (callback) {
16111613
HandleProgressCallback(data, size);
16121614
}
16131615
delete[] data;
16141616
}
16151617

16161618
class ExecutionProgress {
1617-
friend class AsyncProgressWorker;
1619+
friend class AsyncProgressWorkerBase;
16181620
public:
16191621
void Signal() const {
16201622
uv_async_send(that_->async);
16211623
}
1622-
// You could do fancy generics with templates here.
1623-
void Send(const char* data, size_t size) const {
1624+
1625+
void Send(const T* data, size_t size) const {
16241626
that_->SendProgress_(data, size);
16251627
}
16261628

16271629
private:
1628-
explicit ExecutionProgress(AsyncProgressWorker* that) : that_(that) {}
1630+
explicit ExecutionProgress(AsyncProgressWorkerBase *that) : that_(that) {}
16291631
NAN_DISALLOW_ASSIGN_COPY_MOVE(ExecutionProgress)
1630-
AsyncProgressWorker* const that_;
1632+
AsyncProgressWorkerBase* const that_;
16311633
};
16321634

16331635
virtual void Execute(const ExecutionProgress& progress) = 0;
1634-
virtual void HandleProgressCallback(const char *data, size_t size) = 0;
1636+
virtual void HandleProgressCallback(const T *data, size_t size) = 0;
16351637

16361638
virtual void Destroy() {
16371639
uv_close(reinterpret_cast<uv_handle_t*>(async), AsyncClose_);
@@ -1643,12 +1645,15 @@ class Callback {
16431645
Execute(progress);
16441646
}
16451647

1646-
void SendProgress_(const char *data, size_t size) {
1647-
char *new_data = new char[size];
1648-
memcpy(new_data, data, size);
1648+
void SendProgress_(const T *data, size_t size) {
1649+
T *new_data = new T[size];
1650+
{
1651+
T *it = new_data;
1652+
std::copy(data, data + size, it);
1653+
}
16491654

16501655
uv_mutex_lock(&async_lock);
1651-
char *old_data = asyncdata_;
1656+
T *old_data = asyncdata_;
16521657
asyncdata_ = new_data;
16531658
asyncsize_ = size;
16541659
uv_mutex_unlock(&async_lock);
@@ -1658,24 +1663,28 @@ class Callback {
16581663
}
16591664

16601665
inline static NAUV_WORK_CB(AsyncProgress_) {
1661-
AsyncProgressWorker *worker =
1662-
static_cast<AsyncProgressWorker*>(async->data);
1666+
AsyncProgressWorkerBase *worker =
1667+
static_cast<AsyncProgressWorkerBase*>(async->data);
16631668
worker->WorkProgress();
16641669
}
16651670

16661671
inline static void AsyncClose_(uv_handle_t* handle) {
1667-
AsyncProgressWorker *worker =
1668-
static_cast<AsyncProgressWorker*>(handle->data);
1672+
AsyncProgressWorkerBase *worker =
1673+
static_cast<AsyncProgressWorkerBase*>(handle->data);
16691674
delete reinterpret_cast<uv_async_t*>(handle);
16701675
delete worker;
16711676
}
16721677

16731678
uv_async_t *async;
16741679
uv_mutex_t async_lock;
1675-
char *asyncdata_;
1680+
T *asyncdata_;
16761681
size_t asyncsize_;
16771682
};
16781683

1684+
// This ensures compatibility to the previous un-templated AsyncProgressWorker
1685+
// class definition.
1686+
typedef AsyncProgressWorkerBase<char> AsyncProgressWorker;
1687+
16791688
inline void AsyncExecute (uv_work_t* req) {
16801689
AsyncWorker *worker = static_cast<AsyncWorker*>(req->data);
16811690
worker->Execute();

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"commander": "^2.8.1",
2727
"glob": "^5.0.14",
2828
"node-gyp": "~3.0.1",
29+
"readable-stream": "^2.1.4",
2930
"tap": "~0.7.1",
3031
"xtend": "~4.0.0"
3132
},

test/binding.gyp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
"target_name" : "asyncprogressworker"
9898
, "sources" : [ "cpp/asyncprogressworker.cpp" ]
9999
}
100+
, {
101+
"target_name" : "asyncprogressworkerstream"
102+
, "sources" : [ "cpp/asyncprogressworkerstream.cpp" ]
103+
}
100104
, {
101105
"target_name" : "asyncprogressworkersignal"
102106
, "sources" : ["cpp/asyncprogressworkersignal.cpp"]
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*********************************************************************
2+
* NAN - Native Abstractions for Node.js
3+
*
4+
* Copyright (c) 2016 NAN contributors
5+
*
6+
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
7+
********************************************************************/
8+
9+
#ifndef _WIN32
10+
#include <unistd.h>
11+
#define Sleep(x) usleep((x)*1000)
12+
#endif
13+
#include <nan.h>
14+
15+
using namespace Nan; // NOLINT(build/namespaces)
16+
17+
// Custom data type: This serves as an example of how external
18+
// libraries could be hooked in, populate their objects and send them to JS.
19+
struct data_t {
20+
int index;
21+
int data;
22+
};
23+
24+
// Unlike test/cpp/ayncprogressworker.cpp this test is explicitly templated.
25+
template<typename T>
26+
class ProgressWorker : public AsyncProgressWorkerBase<T> {
27+
public:
28+
ProgressWorker(
29+
Callback *callback
30+
, Callback *progress
31+
, int milliseconds
32+
, int iters)
33+
: AsyncProgressWorkerBase<T>(callback), progress(progress)
34+
, milliseconds(milliseconds), iters(iters) {}
35+
~ProgressWorker() {}
36+
37+
void Execute (
38+
const typename AsyncProgressWorkerBase<T>::ExecutionProgress& progress) {
39+
data_t data;
40+
for (int i = 0; i < iters; ++i) {
41+
data.index = i;
42+
data.data = i * 2;
43+
progress.Send(&data, sizeof( data ));
44+
Sleep(milliseconds);
45+
}
46+
}
47+
48+
void HandleProgressCallback(const T *data, size_t size) {
49+
HandleScope scope;
50+
v8::Local<v8::Object> obj = Nan::New<v8::Object>();
51+
Nan::Set(
52+
obj,
53+
Nan::New("index").ToLocalChecked(),
54+
New<v8::Integer>(data->index));
55+
Nan::Set(
56+
obj,
57+
Nan::New("data").ToLocalChecked(),
58+
New<v8::Integer>(data->data));
59+
60+
v8::Local<v8::Value> argv[] = { obj };
61+
progress->Call(1, argv);
62+
}
63+
64+
private:
65+
Callback *progress;
66+
int milliseconds;
67+
int iters;
68+
};
69+
70+
NAN_METHOD(DoProgress) {
71+
Callback *progress = new Callback(info[2].As<v8::Function>());
72+
Callback *callback = new Callback(info[3].As<v8::Function>());
73+
AsyncQueueWorker(new ProgressWorker<data_t>(
74+
callback
75+
, progress
76+
, To<uint32_t>(info[0]).FromJust()
77+
, To<uint32_t>(info[1]).FromJust()));
78+
}
79+
80+
NAN_MODULE_INIT(Init) {
81+
Set(target
82+
, New<v8::String>("a").ToLocalChecked()
83+
, New<v8::FunctionTemplate>(DoProgress)->GetFunction());
84+
}
85+
86+
NODE_MODULE(asyncprogressworkerstream, Init)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*********************************************************************
2+
* NAN - Native Abstractions for Node.js
3+
*
4+
* Copyright (c) 2016 NAN contributors
5+
*
6+
* MIT License <https://github.com/nodejs/nan/blob/master/LICENSE.md>
7+
********************************************************************/
8+
9+
const test = require('tap').test
10+
, testRoot = require('path').resolve(__dirname, '..')
11+
, bindings = require('bindings')({ module_root: testRoot, bindings: 'asyncprogressworkerstream' })
12+
, util = require('util');
13+
14+
const nodeVersion = process.versions.node.split('.')
15+
var Readable
16+
if (nodeVersion[0] == 0 && nodeVersion[1] <= 8)
17+
Readable = require('readable-stream')
18+
else
19+
Readable = require('stream').Readable
20+
21+
function StreamProgressWorker(t) {
22+
Readable.call(this, {objectMode: true})
23+
var self = this
24+
// initialize stream from cpp on next tick
25+
process.nextTick(function () {
26+
var worker = bindings.a
27+
worker(100, 5, function(i) {
28+
self.push(i)
29+
}, function () {
30+
self.push(null)
31+
})
32+
})
33+
}
34+
util.inherits(StreamProgressWorker, Readable)
35+
36+
StreamProgressWorker.prototype._read = function (data) {
37+
38+
};
39+
40+
41+
test('asyncprogressworker', function (t) {
42+
var stream = new StreamProgressWorker(t)
43+
var progressed = 0;
44+
45+
stream
46+
.on('end', function() {
47+
t.ok(progressed === 5, 'cpp should have sent 5 objects')
48+
t.end()
49+
})
50+
.on('data', function(data) {
51+
progressed++
52+
console.log(data);
53+
})
54+
})

0 commit comments

Comments
 (0)