Skip to content

Commit edf630c

Browse files
KevinEadymhdawson
authored andcommitted
src: fix implementation of Signal
PR-URL: #1216 Reviewed-By: Michael Dawson <[email protected] Reviewed-By: Chengzhong Wu <[email protected]>
1 parent de5a502 commit edf630c

File tree

6 files changed

+127
-25
lines changed

6 files changed

+127
-25
lines changed

doc/async_worker_variants.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@ virtual void Napi::AsyncProgressWorker::OnOK();
5151

5252
### OnProgress
5353

54-
This method is invoked when the computation in the `Napi::AsyncProgressWorker::ExecutionProcess::Send`
55-
method was called during worker thread execution.
54+
This method is invoked when the computation in the
55+
`Napi::AsyncProgressWorker::ExecutionProcess::Send` method was called during
56+
worker thread execution. This method can also be triggered via a call to
57+
`Napi::AsyncProgress[Queue]Worker::ExecutionProcess::Signal`, in which case the
58+
`data` parameter will be `nullptr`.
5659

5760
```cpp
5861
virtual void Napi::AsyncProgressWorker::OnProgress(const T* data, size_t count)
@@ -251,6 +254,15 @@ class instead which is documented further down this page.
251254
void Napi::AsyncProgressWorker::ExecutionProcess::Send(const T* data, size_t count) const;
252255
```
253256
257+
### Signal
258+
259+
`Napi::AsyncProgressWorker::ExecutionProcess::Signal` triggers an invocation of
260+
`Napi::AsyncProgressWorker::OnProgress` with `nullptr` as the `data` parameter.
261+
262+
```cpp
263+
void Napi::AsyncProgressWorker::ExecutionProcess::Signal();
264+
```
265+
254266
## Example
255267

256268
The first step to use the `Napi::AsyncProgressWorker` class is to create a new class that
@@ -415,6 +427,15 @@ with each data item.
415427
void Napi::AsyncProgressQueueWorker::ExecutionProcess::Send(const T* data, size_t count) const;
416428
```
417429
430+
### Signal
431+
432+
`Napi::AsyncProgressQueueWorker::ExecutionProcess::Signal` triggers an invocation of
433+
`Napi::AsyncProgressQueueWorker::OnProgress` with `nullptr` as the `data` parameter.
434+
435+
```cpp
436+
void Napi::AsyncProgressQueueWorker::ExecutionProcess::Signal() const;
437+
```
438+
418439
## Example
419440

420441
The code below shows an example of the `Napi::AsyncProgressQueueWorker` implementation, but

napi-inl.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5940,7 +5940,8 @@ inline AsyncProgressWorker<T>::AsyncProgressWorker(const Object& receiver,
59405940
const Object& resource)
59415941
: AsyncProgressWorkerBase(receiver, callback, resource_name, resource),
59425942
_asyncdata(nullptr),
5943-
_asyncsize(0) {}
5943+
_asyncsize(0),
5944+
_signaled(false) {}
59445945

59455946
#if NAPI_VERSION > 4
59465947
template <class T>
@@ -5980,12 +5981,15 @@ template <class T>
59805981
inline void AsyncProgressWorker<T>::OnWorkProgress(void*) {
59815982
T* data;
59825983
size_t size;
5984+
bool signaled;
59835985
{
59845986
std::lock_guard<std::mutex> lock(this->_mutex);
59855987
data = this->_asyncdata;
59865988
size = this->_asyncsize;
5989+
signaled = this->_signaled;
59875990
this->_asyncdata = nullptr;
59885991
this->_asyncsize = 0;
5992+
this->_signaled = false;
59895993
}
59905994

59915995
/**
@@ -5995,7 +5999,7 @@ inline void AsyncProgressWorker<T>::OnWorkProgress(void*) {
59955999
* the deferring the signal of uv_async_t is been sent again, i.e. potential
59966000
* not coalesced two calls of the TSFN callback.
59976001
*/
5998-
if (data == nullptr) {
6002+
if (data == nullptr && !signaled) {
59996003
return;
60006004
}
60016005

@@ -6014,20 +6018,25 @@ inline void AsyncProgressWorker<T>::SendProgress_(const T* data, size_t count) {
60146018
old_data = _asyncdata;
60156019
_asyncdata = new_data;
60166020
_asyncsize = count;
6021+
_signaled = false;
60176022
}
60186023
this->NonBlockingCall(nullptr);
60196024

60206025
delete[] old_data;
60216026
}
60226027

60236028
template <class T>
6024-
inline void AsyncProgressWorker<T>::Signal() const {
6029+
inline void AsyncProgressWorker<T>::Signal() {
6030+
{
6031+
std::lock_guard<std::mutex> lock(this->_mutex);
6032+
_signaled = true;
6033+
}
60256034
this->NonBlockingCall(static_cast<T*>(nullptr));
60266035
}
60276036

60286037
template <class T>
60296038
inline void AsyncProgressWorker<T>::ExecutionProgress::Signal() const {
6030-
_worker->Signal();
6039+
this->_worker->Signal();
60316040
}
60326041

60336042
template <class T>
@@ -6130,7 +6139,7 @@ inline void AsyncProgressQueueWorker<T>::SendProgress_(const T* data,
61306139

61316140
template <class T>
61326141
inline void AsyncProgressQueueWorker<T>::Signal() const {
6133-
this->NonBlockingCall(nullptr);
6142+
this->SendProgress_(static_cast<T*>(nullptr), 0);
61346143
}
61356144

61366145
template <class T>
@@ -6142,7 +6151,7 @@ inline void AsyncProgressQueueWorker<T>::OnWorkComplete(Napi::Env env,
61426151

61436152
template <class T>
61446153
inline void AsyncProgressQueueWorker<T>::ExecutionProgress::Signal() const {
6145-
_worker->Signal();
6154+
_worker->SendProgress_(static_cast<T*>(nullptr), 0);
61466155
}
61476156

61486157
template <class T>

napi.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3004,12 +3004,13 @@ class AsyncProgressWorker : public AsyncProgressWorkerBase<void> {
30043004

30053005
private:
30063006
void Execute() override;
3007-
void Signal() const;
3007+
void Signal();
30083008
void SendProgress_(const T* data, size_t count);
30093009

30103010
std::mutex _mutex;
30113011
T* _asyncdata;
30123012
size_t _asyncsize;
3013+
bool _signaled;
30133014
};
30143015

30153016
template <class T>

test/async_progress_queue_worker.cc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class TestWorker : public AsyncProgressQueueWorker<ProgressData> {
4141

4242
if (_times < 0) {
4343
SetError("test error");
44+
} else {
45+
progress.Signal();
4446
}
4547
ProgressData data{0};
4648
for (int32_t idx = 0; idx < _times; idx++) {
@@ -49,11 +51,18 @@ class TestWorker : public AsyncProgressQueueWorker<ProgressData> {
4951
}
5052
}
5153

52-
void OnProgress(const ProgressData* data, size_t /* count */) override {
54+
void OnProgress(const ProgressData* data, size_t count) override {
5355
Napi::Env env = Env();
56+
_test_case_count++;
5457
if (!_js_progress_cb.IsEmpty()) {
55-
Number progress = Number::New(env, data->progress);
56-
_js_progress_cb.Call(Receiver().Value(), {progress});
58+
if (_test_case_count == 1) {
59+
if (count != 0) {
60+
SetError("expect 0 count of data on 1st call");
61+
}
62+
} else {
63+
Number progress = Number::New(env, data->progress);
64+
_js_progress_cb.Call(Receiver().Value(), {progress});
65+
}
5766
}
5867
}
5968

@@ -68,6 +77,7 @@ class TestWorker : public AsyncProgressQueueWorker<ProgressData> {
6877
}
6978

7079
int32_t _times;
80+
size_t _test_case_count = 0;
7181
FunctionReference _js_progress_cb;
7282
};
7383

test/async_progress_worker.cc

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,17 @@ class MalignWorker : public AsyncProgressWorker<ProgressData> {
7878

7979
protected:
8080
void Execute(const ExecutionProgress& progress) override {
81-
std::unique_lock<std::mutex> lock(_cvm);
82-
// Testing a nullptr send is acceptable.
83-
progress.Send(nullptr, 0);
84-
_cv.wait(lock);
81+
{
82+
std::unique_lock<std::mutex> lock(_cvm);
83+
// Testing a nullptr send is acceptable.
84+
progress.Send(nullptr, 0);
85+
_cv.wait(lock, [this] { return _test_case_count == 1; });
86+
}
87+
{
88+
std::unique_lock<std::mutex> lock(_cvm);
89+
progress.Signal();
90+
_cv.wait(lock, [this] { return _test_case_count == 2; });
91+
}
8592
// Testing busy looping on send doesn't trigger unexpected empty data
8693
// OnProgress call.
8794
for (size_t i = 0; i < 1000000; i++) {
@@ -92,16 +99,21 @@ class MalignWorker : public AsyncProgressWorker<ProgressData> {
9299

93100
void OnProgress(const ProgressData* /* data */, size_t count) override {
94101
Napi::Env env = Env();
95-
_test_case_count++;
102+
{
103+
std::lock_guard<std::mutex> lock(_cvm);
104+
_test_case_count++;
105+
}
96106
bool error = false;
97107
Napi::String reason = Napi::String::New(env, "No error");
98-
if (_test_case_count == 1 && count != 0) {
108+
if (_test_case_count <= 2 && count != 0) {
99109
error = true;
100-
reason = Napi::String::New(env, "expect 0 count of data on 1st call");
110+
reason =
111+
Napi::String::New(env, "expect 0 count of data on 1st and 2nd call");
101112
}
102-
if (_test_case_count > 1 && count != 1) {
113+
if (_test_case_count > 2 && count != 1) {
103114
error = true;
104-
reason = Napi::String::New(env, "expect 1 count of data on non-1st call");
115+
reason = Napi::String::New(
116+
env, "expect 1 count of data on non-1st and non-2nd call");
105117
}
106118
_progress.MakeCallback(Receiver().Value(),
107119
{Napi::Boolean::New(env, error), reason});
@@ -122,12 +134,56 @@ class MalignWorker : public AsyncProgressWorker<ProgressData> {
122134
std::mutex _cvm;
123135
FunctionReference _progress;
124136
};
137+
138+
// Calling a Signal after a SendProgress should not clear progress data
139+
class SignalAfterProgressTestWorker : public AsyncProgressWorker<ProgressData> {
140+
public:
141+
static void DoWork(const CallbackInfo& info) {
142+
Function cb = info[0].As<Function>();
143+
Function progress = info[1].As<Function>();
144+
145+
SignalAfterProgressTestWorker* worker = new SignalAfterProgressTestWorker(
146+
cb, progress, "TestResource", Object::New(info.Env()));
147+
worker->Queue();
148+
}
149+
150+
protected:
151+
void Execute(const ExecutionProgress& progress) override {
152+
ProgressData data{0};
153+
progress.Send(&data, 1);
154+
progress.Signal();
155+
}
156+
157+
void OnProgress(const ProgressData* /* data */, size_t count) override {
158+
Napi::Env env = Env();
159+
bool error = false;
160+
Napi::String reason = Napi::String::New(env, "No error");
161+
if (count != 1) {
162+
error = true;
163+
reason = Napi::String::New(env, "expect 1 count of data");
164+
}
165+
_progress.MakeCallback(Receiver().Value(),
166+
{Napi::Boolean::New(env, error), reason});
167+
}
168+
169+
private:
170+
SignalAfterProgressTestWorker(Function cb,
171+
Function progress,
172+
const char* resource_name,
173+
const Object& resource)
174+
: AsyncProgressWorker(cb, resource_name, resource) {
175+
_progress.Reset(progress, 1);
176+
}
177+
FunctionReference _progress;
178+
};
125179
} // namespace
126180

127181
Object InitAsyncProgressWorker(Env env) {
128182
Object exports = Object::New(env);
129183
exports["doWork"] = Function::New(env, TestWorker::DoWork);
130184
exports["doMalignTest"] = Function::New(env, MalignWorker::DoWork);
185+
exports["doSignalAfterProgressTest"] =
186+
Function::New(env, SignalAfterProgressTestWorker::DoWork);
131187
return exports;
132188
}
133189

test/async_progress_worker.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ module.exports = common.runTest(test);
88
async function test ({ asyncprogressworker }) {
99
await success(asyncprogressworker);
1010
await fail(asyncprogressworker);
11-
await malignTest(asyncprogressworker);
11+
await signalTest(asyncprogressworker.doMalignTest);
12+
await signalTest(asyncprogressworker.doSignalAfterProgressTest);
1213
}
1314

1415
function success (binding) {
@@ -44,17 +45,21 @@ function fail (binding) {
4445
});
4546
}
4647

47-
function malignTest (binding) {
48+
function signalTest (bindingFunction) {
4849
return new Promise((resolve, reject) => {
49-
binding.doMalignTest(
50+
bindingFunction(
5051
common.mustCall((err) => {
5152
if (err) {
5253
return reject(err);
5354
}
5455
resolve();
5556
}),
5657
common.mustCallAtLeast((error, reason) => {
57-
assert(!error, reason);
58+
try {
59+
assert(!error, reason);
60+
} catch (e) {
61+
reject(e);
62+
}
5863
}, 1)
5964
);
6065
});

0 commit comments

Comments
 (0)