Skip to content

Commit dc63ed8

Browse files
committed
Implement AsyncProgressQueueWorker
1 parent 295e560 commit dc63ed8

File tree

7 files changed

+438
-61
lines changed

7 files changed

+438
-61
lines changed

napi-inl.h

Lines changed: 219 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3699,8 +3699,8 @@ inline AsyncWorker::AsyncWorker(const Object& receiver,
36993699
_env, resource_name, NAPI_AUTO_LENGTH, &resource_id);
37003700
NAPI_THROW_IF_FAILED_VOID(_env, status);
37013701

3702-
status = napi_create_async_work(_env, resource, resource_id, OnExecute,
3703-
OnWorkComplete, this, &_work);
3702+
status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute,
3703+
OnAsyncWorkComplete, this, &_work);
37043704
NAPI_THROW_IF_FAILED_VOID(_env, status);
37053705
}
37063706

@@ -3725,8 +3725,8 @@ inline AsyncWorker::AsyncWorker(Napi::Env env,
37253725
_env, resource_name, NAPI_AUTO_LENGTH, &resource_id);
37263726
NAPI_THROW_IF_FAILED_VOID(_env, status);
37273727

3728-
status = napi_create_async_work(_env, resource, resource_id, OnExecute,
3729-
OnWorkComplete, this, &_work);
3728+
status = napi_create_async_work(_env, resource, resource_id, OnAsyncWorkExecute,
3729+
OnAsyncWorkComplete, this, &_work);
37303730
NAPI_THROW_IF_FAILED_VOID(_env, status);
37313731
}
37323732

@@ -3813,40 +3813,51 @@ inline void AsyncWorker::SetError(const std::string& error) {
38133813
inline std::vector<napi_value> AsyncWorker::GetResult(Napi::Env /*env*/) {
38143814
return {};
38153815
}
3816+
// The OnAsyncWorkExecute method receives an napi_env argument. However, do NOT
3817+
// use it within this method, as it does not run on the main thread and must
3818+
// not run any method that would cause JavaScript to run. In practice, this
3819+
// means that almost any use of napi_env will be incorrect.
3820+
inline void OnAsyncWorkExecute(napi_env env, void* asyncworker) {
3821+
AsyncWorker* self = static_cast<AsyncWorker*>(asyncworker);
3822+
self->OnExecute(env);
3823+
}
38163824
// The OnExecute method receives an napi_env argument. However, do NOT
38173825
// use it within this method, as it does not run on the main thread and must
38183826
// not run any method that would cause JavaScript to run. In practice, this
38193827
// means that almost any use of napi_env will be incorrect.
3820-
inline void AsyncWorker::OnExecute(napi_env /*DO_NOT_USE*/, void* this_pointer) {
3821-
AsyncWorker* self = static_cast<AsyncWorker*>(this_pointer);
3828+
inline void AsyncWorker::OnExecute(napi_env /*DO_NOT_USE*/) {
38223829
#ifdef NAPI_CPP_EXCEPTIONS
38233830
try {
3824-
self->Execute();
3831+
this->Execute();
38253832
} catch (const std::exception& e) {
3826-
self->SetError(e.what());
3833+
this->SetError(e.what());
38273834
}
38283835
#else // NAPI_CPP_EXCEPTIONS
3829-
self->Execute();
3836+
this->Execute();
38303837
#endif // NAPI_CPP_EXCEPTIONS
38313838
}
38323839

3833-
inline void AsyncWorker::OnWorkComplete(
3834-
napi_env /*env*/, napi_status status, void* this_pointer) {
3835-
AsyncWorker* self = static_cast<AsyncWorker*>(this_pointer);
3840+
inline void OnAsyncWorkComplete(napi_env env,
3841+
napi_status status,
3842+
void* asyncworker) {
3843+
AsyncWorker* self = static_cast<AsyncWorker*>(asyncworker);
3844+
self->OnWorkComplete(env, status);
3845+
}
3846+
inline void AsyncWorker::OnWorkComplete(napi_env /*env*/, napi_status status) {
38363847
if (status != napi_cancelled) {
3837-
HandleScope scope(self->_env);
3848+
HandleScope scope(this->_env);
38383849
details::WrapCallback([&] {
3839-
if (self->_error.size() == 0) {
3840-
self->OnOK();
3850+
if (this->_error.size() == 0) {
3851+
this->OnOK();
38413852
}
38423853
else {
3843-
self->OnError(Error::New(self->_env, self->_error));
3854+
this->OnError(Error::New(this->_env, this->_error));
38443855
}
38453856
return nullptr;
38463857
});
38473858
}
3848-
if (!self->_suppress_destruct) {
3849-
self->Destroy();
3859+
if (!this->_suppress_destruct) {
3860+
this->Destroy();
38503861
}
38513862
}
38523863

@@ -4172,9 +4183,38 @@ inline void ThreadSafeFunction::CallJS(napi_env env,
41724183
}
41734184

41744185
////////////////////////////////////////////////////////////////////////////////
4175-
// Async Progress Worker class
4186+
// Async Progress Worker Base class
41764187
////////////////////////////////////////////////////////////////////////////////
4188+
inline AsyncProgressWorkerBase::AsyncProgressWorkerBase(const Object& receiver,
4189+
const Function& callback,
4190+
const char* resource_name,
4191+
const Object& resource)
4192+
: AsyncWorker(receiver, callback, resource_name, resource) {
4193+
_tsfn = ThreadSafeFunction::New(callback.Env(), callback, resource_name, 1, 1);
4194+
}
41774195

4196+
#if NAPI_VERSION > 4
4197+
inline AsyncProgressWorkerBase::AsyncProgressWorkerBase(Napi::Env env,
4198+
const char* resource_name,
4199+
const Object& resource)
4200+
: AsyncWorker(env, resource_name, resource) {
4201+
// TODO: Once the changes to make the callback optional for threadsafe
4202+
// functions are no longer optional we can remove the dummy Function here.
4203+
Function callback;
4204+
_tsfn = ThreadSafeFunction::New(env, callback, resource_name, 1, 1);
4205+
}
4206+
#endif
4207+
4208+
inline void OnAsyncWorkProgress(Napi::Env /* env */,
4209+
Napi::Function /* jsCallback */,
4210+
void* asyncworker) {
4211+
AsyncProgressWorkerBase* asyncprogressworker = static_cast<AsyncProgressWorkerBase*>(asyncworker);
4212+
asyncprogressworker->OnWorkProgress();
4213+
}
4214+
4215+
////////////////////////////////////////////////////////////////////////////////
4216+
// Async Progress Worker class
4217+
////////////////////////////////////////////////////////////////////////////////
41784218
template<class T>
41794219
inline AsyncProgressWorker<T>::AsyncProgressWorker(const Function& callback)
41804220
: AsyncProgressWorker(callback, "generic") {
@@ -4217,10 +4257,9 @@ inline AsyncProgressWorker<T>::AsyncProgressWorker(const Object& receiver,
42174257
const Function& callback,
42184258
const char* resource_name,
42194259
const Object& resource)
4220-
: AsyncWorker(receiver, callback, resource_name, resource),
4260+
: AsyncProgressWorkerBase(receiver, callback, resource_name, resource),
42214261
_asyncdata(nullptr),
42224262
_asyncsize(0) {
4223-
_tsfn = ThreadSafeFunction::New(callback.Env(), callback, resource_name, 1, 1);
42244263
}
42254264

42264265
#if NAPI_VERSION > 4
@@ -4239,27 +4278,23 @@ template<class T>
42394278
inline AsyncProgressWorker<T>::AsyncProgressWorker(Napi::Env env,
42404279
const char* resource_name,
42414280
const Object& resource)
4242-
: AsyncWorker(env, resource_name, resource),
4281+
: AsyncProgressWorkerBase(env, resource_name, resource),
42434282
_asyncdata(nullptr),
42444283
_asyncsize(0) {
4245-
// TODO: Once the changes to make the callback optional for threadsafe
4246-
// functions are no longer optional we can remove the dummy Function here.
4247-
Function callback;
4248-
_tsfn = ThreadSafeFunction::New(env, callback, resource_name, 1, 1);
42494284
}
42504285
#endif
42514286

42524287
template<class T>
42534288
inline AsyncProgressWorker<T>::~AsyncProgressWorker() {
42544289
// Abort pending tsfn call.
42554290
// Don't send progress events after we've already completed.
4256-
_tsfn.Abort();
4291+
this->_tsfn.Abort();
42574292
{
4258-
std::lock_guard<std::mutex> lock(_mutex);
4293+
std::lock_guard<std::mutex> lock(this->_mutex);
42594294
_asyncdata = nullptr;
42604295
_asyncsize = 0;
42614296
}
4262-
_tsfn.Release();
4297+
this->_tsfn.Release();
42634298
}
42644299

42654300
template<class T>
@@ -4269,20 +4304,18 @@ inline void AsyncProgressWorker<T>::Execute() {
42694304
}
42704305

42714306
template<class T>
4272-
inline void AsyncProgressWorker<T>::WorkProgress_(Napi::Env /* env */, Napi::Function /* jsCallback */, void* _data) {
4273-
AsyncProgressWorker* self = static_cast<AsyncProgressWorker*>(_data);
4274-
4307+
inline void AsyncProgressWorker<T>::OnWorkProgress() {
42754308
T* data;
42764309
size_t size;
42774310
{
4278-
std::lock_guard<std::mutex> lock(self->_mutex);
4279-
data = self->_asyncdata;
4280-
size = self->_asyncsize;
4281-
self->_asyncdata = nullptr;
4282-
self->_asyncsize = 0;
4311+
std::lock_guard<std::mutex> lock(this->_mutex);
4312+
data = this->_asyncdata;
4313+
size = this->_asyncsize;
4314+
this->_asyncdata = nullptr;
4315+
this->_asyncsize = 0;
42834316
}
42844317

4285-
self->OnProgress(data, size);
4318+
this->OnProgress(data, size);
42864319
delete[] data;
42874320
}
42884321

@@ -4293,19 +4326,19 @@ inline void AsyncProgressWorker<T>::SendProgress_(const T* data, size_t count) {
42934326

42944327
T* old_data;
42954328
{
4296-
std::lock_guard<std::mutex> lock(_mutex);
4329+
std::lock_guard<std::mutex> lock(this->_mutex);
42974330
old_data = _asyncdata;
42984331
_asyncdata = new_data;
42994332
_asyncsize = count;
43004333
}
4301-
_tsfn.NonBlockingCall(this, WorkProgress_);
4334+
this->_tsfn.NonBlockingCall(this, OnAsyncWorkProgress);
43024335

43034336
delete[] old_data;
43044337
}
43054338

43064339
template<class T>
43074340
inline void AsyncProgressWorker<T>::Signal() const {
4308-
_tsfn.NonBlockingCall(this, WorkProgress_);
4341+
this->_tsfn.NonBlockingCall(this, OnAsyncWorkProgress);
43094342
}
43104343

43114344
template<class T>
@@ -4318,6 +4351,151 @@ inline void AsyncProgressWorker<T>::ExecutionProgress::Send(const T* data, size_
43184351
_worker->SendProgress_(data, count);
43194352
}
43204353

4354+
////////////////////////////////////////////////////////////////////////////////
4355+
// Async Progress Queue Worker class
4356+
////////////////////////////////////////////////////////////////////////////////
4357+
template<class T>
4358+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(const Function& callback)
4359+
: AsyncProgressQueueWorker(callback, "generic") {
4360+
}
4361+
4362+
template<class T>
4363+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(const Function& callback,
4364+
const char* resource_name)
4365+
: AsyncProgressQueueWorker(callback, resource_name, Object::New(callback.Env())) {
4366+
}
4367+
4368+
template<class T>
4369+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(const Function& callback,
4370+
const char* resource_name,
4371+
const Object& resource)
4372+
: AsyncProgressQueueWorker(Object::New(callback.Env()),
4373+
callback,
4374+
resource_name,
4375+
resource) {
4376+
}
4377+
4378+
template<class T>
4379+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(const Object& receiver,
4380+
const Function& callback)
4381+
: AsyncProgressQueueWorker(receiver, callback, "generic") {
4382+
}
4383+
4384+
template<class T>
4385+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(const Object& receiver,
4386+
const Function& callback,
4387+
const char* resource_name)
4388+
: AsyncProgressQueueWorker(receiver,
4389+
callback,
4390+
resource_name,
4391+
Object::New(callback.Env())) {
4392+
}
4393+
4394+
template<class T>
4395+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(const Object& receiver,
4396+
const Function& callback,
4397+
const char* resource_name,
4398+
const Object& resource)
4399+
: AsyncProgressWorkerBase(receiver, callback, resource_name, resource) {
4400+
}
4401+
4402+
#if NAPI_VERSION > 4
4403+
template<class T>
4404+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(Napi::Env env)
4405+
: AsyncProgressQueueWorker(env, "generic") {
4406+
}
4407+
4408+
template<class T>
4409+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(Napi::Env env,
4410+
const char* resource_name)
4411+
: AsyncProgressQueueWorker(env, resource_name, Object::New(env)) {
4412+
}
4413+
4414+
template<class T>
4415+
inline AsyncProgressQueueWorker<T>::AsyncProgressQueueWorker(Napi::Env env,
4416+
const char* resource_name,
4417+
const Object& resource)
4418+
: AsyncProgressWorkerBase(env, resource_name, resource) {
4419+
}
4420+
#endif
4421+
4422+
template<class T>
4423+
inline AsyncProgressQueueWorker<T>::~AsyncProgressQueueWorker() {
4424+
// Abort pending tsfn call.
4425+
// Don't send progress events after we've already completed.
4426+
this->_tsfn.Abort();
4427+
{
4428+
std::lock_guard<std::mutex> lock(this->_mutex);
4429+
while (!_asyncdata.empty()) {
4430+
std::pair<T*, size_t> &datapair = _asyncdata.front();
4431+
T *data = datapair.first;
4432+
4433+
_asyncdata.pop();
4434+
4435+
delete[] data;
4436+
}
4437+
}
4438+
this->_tsfn.Release();
4439+
}
4440+
4441+
template<class T>
4442+
inline void AsyncProgressQueueWorker<T>::Execute() {
4443+
ExecutionProgress progress(this);
4444+
Execute(progress);
4445+
}
4446+
4447+
template<class T>
4448+
inline void AsyncProgressQueueWorker<T>::OnWorkProgress() {
4449+
this->_mutex.lock();
4450+
while (!this->_asyncdata.empty()) {
4451+
std::pair<T*, size_t> &datapair = this->_asyncdata.front();
4452+
4453+
T *data = datapair.first;
4454+
size_t size = datapair.second;
4455+
4456+
this->_asyncdata.pop();
4457+
this->_mutex.unlock();
4458+
4459+
this->OnProgress(data, size);
4460+
delete[] data;
4461+
4462+
this->_mutex.lock();
4463+
}
4464+
this->_mutex.unlock();
4465+
}
4466+
4467+
template<class T>
4468+
inline void AsyncProgressQueueWorker<T>::SendProgress_(const T* data, size_t count) {
4469+
T* new_data = new T[count];
4470+
std::copy(data, data + count, new_data);
4471+
4472+
{
4473+
std::lock_guard<std::mutex> lock(this->_mutex);
4474+
_asyncdata.push(std::pair<T*, size_t>(new_data, count));
4475+
}
4476+
this->_tsfn.NonBlockingCall(this, OnAsyncWorkProgress);
4477+
}
4478+
4479+
template<class T>
4480+
inline void AsyncProgressQueueWorker<T>::Signal() const {
4481+
this->_tsfn.NonBlockingCall(this, OnAsyncWorkProgress);
4482+
}
4483+
4484+
template<class T>
4485+
inline void AsyncProgressQueueWorker<T>::OnWorkComplete(napi_env env, napi_status status) {
4486+
this->OnWorkProgress();
4487+
AsyncWorker::OnWorkComplete(env, status);
4488+
}
4489+
4490+
template<class T>
4491+
inline void AsyncProgressQueueWorker<T>::ExecutionProgress::Signal() const {
4492+
_worker->Signal();
4493+
}
4494+
4495+
template<class T>
4496+
inline void AsyncProgressQueueWorker<T>::ExecutionProgress::Send(const T* data, size_t count) const {
4497+
_worker->SendProgress_(data, count);
4498+
}
43214499
#endif
43224500

43234501
////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)