From 793268c59f8c14a56f44ef00a7711c18d1c35b95 Mon Sep 17 00:00:00 2001 From: Jack <32422811+JckXia@users.noreply.github.com> Date: Mon, 26 Sep 2022 11:47:34 -0400 Subject: [PATCH] test: Add test case for canceling async worker tasks (#1202) * test: Add test case for canceling async worker tasks --- test/async_worker.cc | 72 ++++++++++++++++++++++++++++++++++++++++++++ test/async_worker.js | 3 ++ 2 files changed, 75 insertions(+) diff --git a/test/async_worker.cc b/test/async_worker.cc index 8efb0d5b0..b77684197 100644 --- a/test/async_worker.cc +++ b/test/async_worker.cc @@ -1,3 +1,6 @@ +#include +#include +#include "assert.h" #include "napi.h" using namespace Napi; @@ -95,6 +98,74 @@ class TestWorkerNoCallback : public AsyncWorker { bool _succeed; }; +class EchoWorker : public AsyncWorker { + public: + EchoWorker(Function& cb, std::string& echo) : AsyncWorker(cb), echo(echo) {} + ~EchoWorker() {} + + void Execute() override { + // Simulate cpu heavy task + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + } + + void OnOK() override { + HandleScope scope(Env()); + Callback().Call({Env().Null(), String::New(Env(), echo)}); + } + + private: + std::string echo; +}; + +class CancelWorker : public AsyncWorker { + public: + CancelWorker(Function& cb) : AsyncWorker(cb) {} + ~CancelWorker() {} + + static void DoWork(const CallbackInfo& info) { + Function cb = info[0].As(); + std::string echo = info[1].As(); + int threadNum = info[2].As().Uint32Value(); + + for (int i = 0; i < threadNum; i++) { + AsyncWorker* worker = new EchoWorker(cb, echo); + worker->Queue(); + assert(worker->Env() == info.Env()); + } + + AsyncWorker* cancelWorker = new CancelWorker(cb); + cancelWorker->Queue(); + +#ifdef NAPI_CPP_EXCEPTIONS + try { + cancelWorker->Cancel(); + } catch (Napi::Error& e) { + Napi::Error::New(info.Env(), "Unable to cancel async worker tasks") + .ThrowAsJavaScriptException(); + } +#else + cancelWorker->Cancel(); +#endif + } + + void Execute() override { + // Simulate cpu heavy task + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + void OnOK() override { + Napi::Error::New(this->Env(), + "OnOk should not be invoked on successful cancellation") + .ThrowAsJavaScriptException(); + } + + void OnError(const Error&) override { + Napi::Error::New(this->Env(), + "OnError should not be invoked on successful cancellation") + .ThrowAsJavaScriptException(); + } +}; + Object InitAsyncWorker(Env env) { Object exports = Object::New(env); exports["doWork"] = Function::New(env, TestWorker::DoWork); @@ -102,5 +173,6 @@ Object InitAsyncWorker(Env env) { Function::New(env, TestWorkerNoCallback::DoWork); exports["doWorkWithResult"] = Function::New(env, TestWorkerWithResult::DoWork); + exports["tryCancelQueuedWork"] = Function::New(env, CancelWorker::DoWork); return exports; } diff --git a/test/async_worker.js b/test/async_worker.js index 83e112a1e..a00e622e5 100644 --- a/test/async_worker.js +++ b/test/async_worker.js @@ -63,6 +63,9 @@ function installAsyncHooksForTest () { } async function test (binding) { + const libUvThreadCount = Number(process.env.UV_THREADPOOL_SIZE || 4); + binding.asyncworker.tryCancelQueuedWork(() => {}, 'echoString', libUvThreadCount); + if (!checkAsyncHooks()) { await new Promise((resolve) => { binding.asyncworker.doWork(true, {}, function (e) {