mirror of
https://github.com/openharmony/js_api_module.git
synced 2026-07-20 22:07:00 -04:00
1459ce5be1
add Timer Interface for worker execute polling operation and delay operation in the worker Without waking up the system,add timer related interfaces, setTimeout, setinterval, cleartimeout, clearinterval in worker initialize issue: https://gitee.com/openharmony/js_worker_module/issues/I51DAA Signed-off-by: y00576111 <yaojian16@huawei.com> Change-Id: I7089854c7f557c3f1d9f906854a2dad682e68400
1408 lines
53 KiB
C++
1408 lines
53 KiB
C++
/*
|
|
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "base/compileruntime/js_worker_module/worker/worker.h"
|
|
#include "base/compileruntime/js_worker_module/plugin/timer.h"
|
|
|
|
namespace CompilerRuntime::WorkerModule {
|
|
const static int MAXWORKERS = 8;
|
|
static std::list<Worker*> g_workers;
|
|
static std::mutex g_workersMutex;
|
|
|
|
Worker::Worker(napi_env env, napi_ref thisVar)
|
|
: hostEnv_(env), workerRef_(thisVar)
|
|
{}
|
|
|
|
void Worker::StartExecuteInThread(napi_env env, const char* script)
|
|
{
|
|
// 1. init hostOnMessageSignal_ in host loop
|
|
uv_loop_t* loop = Helper::NapiHelper::GetLibUV(env);
|
|
if (loop == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker::engine loop is null");
|
|
Helper::CloseHelp::DeletePointer(script, true);
|
|
return;
|
|
}
|
|
uv_async_init(loop, &hostOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnMessage));
|
|
uv_async_init(loop, &hostOnErrorSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnError));
|
|
|
|
// 2. copy the script
|
|
script_ = std::string(script);
|
|
Helper::CloseHelp::DeletePointer(script, true);
|
|
|
|
// 3. create WorkerRunner to Execute
|
|
if (!runner_) {
|
|
runner_ = std::make_unique<WorkerRunner>(WorkerStartCallback(ExecuteInThread, this));
|
|
}
|
|
if (runner_) {
|
|
runner_->Execute(); // start a new thread
|
|
} else {
|
|
HILOG_ERROR("runner_ is nullptr");
|
|
}
|
|
}
|
|
|
|
void Worker::CloseInner()
|
|
{
|
|
UpdateWorkerState(TERMINATEING);
|
|
TerminateWorker();
|
|
}
|
|
|
|
napi_value Worker::CloseWorker(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
Worker* worker = nullptr;
|
|
napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, (void**)&worker);
|
|
if (worker != nullptr) {
|
|
worker->CloseInner();
|
|
}
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
void CallWorkCallback(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type)
|
|
{
|
|
napi_value callback = nullptr;
|
|
napi_get_named_property(env, recv, type, &callback);
|
|
if (Helper::NapiHelper::IsCallable(env, callback)) {
|
|
napi_value callbackResult = nullptr;
|
|
napi_call_function(env, recv, callback, argc, argv, &callbackResult);
|
|
}
|
|
}
|
|
|
|
bool Worker::PrepareForWorkerInstance()
|
|
{
|
|
std::vector<uint8_t> scriptContent;
|
|
std::string workerAmi;
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
|
|
if (HostIsStop()) {
|
|
return false;
|
|
}
|
|
// 1. init worker async func
|
|
auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_);
|
|
|
|
auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
|
|
if (!hostEngine->CallWorkerAsyncWorkFunc(workerEngine)) {
|
|
HILOG_ERROR("worker:: CallWorkerAsyncWorkFunc error");
|
|
}
|
|
// 2. init worker environment
|
|
if (!hostEngine->CallInitWorkerFunc(workerEngine)) {
|
|
HILOG_ERROR("worker:: CallInitWorkerFunc error");
|
|
return false;
|
|
}
|
|
// 3. get uril content
|
|
if (!hostEngine->CallGetAssetFunc(script_, scriptContent, workerAmi)) {
|
|
HILOG_ERROR("worker:: CallGetAssetFunc error");
|
|
return false;
|
|
}
|
|
}
|
|
// add timer interface
|
|
Plugin::Timer::RegisterTime(workerEnv_);
|
|
HILOG_INFO("worker:: stringContent size is %{public}zu", scriptContent.size());
|
|
napi_value execScriptResult = nullptr;
|
|
napi_run_actor(workerEnv_, scriptContent, workerAmi.c_str(), &execScriptResult);
|
|
if (execScriptResult == nullptr) {
|
|
// An exception occurred when running the script.
|
|
HILOG_ERROR("worker:: run script exception occurs, will handle exception");
|
|
HandleException();
|
|
return false;
|
|
}
|
|
|
|
// 4. register worker name in DedicatedWorkerGlobalScope
|
|
if (!name_.empty()) {
|
|
napi_value nameValue = nullptr;
|
|
napi_create_string_utf8(workerEnv_, name_.c_str(), name_.length(), &nameValue);
|
|
Helper::NapiHelper::SetNamePropertyInGlobal(workerEnv_, "name", nameValue);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Worker::UpdateWorkerState(RunnerState state)
|
|
{
|
|
bool done = false;
|
|
do {
|
|
RunnerState oldState = runnerState_.load(std::memory_order_acquire);
|
|
if (oldState >= state) {
|
|
// make sure state sequence is start, running, terminating, terminated
|
|
return false;
|
|
}
|
|
done = runnerState_.compare_exchange_strong(oldState, state);
|
|
} while (!done);
|
|
return true;
|
|
}
|
|
|
|
bool Worker::UpdateHostState(HostState state)
|
|
{
|
|
bool done = false;
|
|
do {
|
|
HostState oldState = hostState_.load(std::memory_order_acquire);
|
|
if (oldState >= state) {
|
|
// make sure state sequence is ACTIVE, INACTIVE
|
|
return false;
|
|
}
|
|
done = hostState_.compare_exchange_strong(oldState, state);
|
|
} while (!done);
|
|
return true;
|
|
}
|
|
|
|
void Worker::PublishWorkerOverSignal()
|
|
{
|
|
// post NULL tell host worker is not running
|
|
if (!HostIsStop()) {
|
|
hostMessageQueue_.EnQueue(NULL);
|
|
uv_async_send(&hostOnMessageSignal_);
|
|
}
|
|
}
|
|
|
|
void Worker::ExecuteInThread(const void* data)
|
|
{
|
|
auto worker = reinterpret_cast<Worker*>(const_cast<void*>(data));
|
|
// 1. create a runtime, nativeengine
|
|
napi_env workerEnv = nullptr;
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
|
|
if (worker->HostIsStop()) {
|
|
Helper::CloseHelp::DeletePointer(worker, false);
|
|
return;
|
|
}
|
|
napi_env env = worker->GetHostEnv();
|
|
napi_create_runtime(env, &workerEnv);
|
|
if (workerEnv == nullptr) {
|
|
napi_throw_error(env, nullptr, "Worker create runtime error");
|
|
return;
|
|
}
|
|
// mark worker env is subThread
|
|
reinterpret_cast<NativeEngine*>(workerEnv)->MarkSubThread();
|
|
worker->SetWorkerEnv(workerEnv);
|
|
}
|
|
|
|
uv_loop_t* loop = worker->GetWorkerLoop();
|
|
if (loop == nullptr) {
|
|
HILOG_ERROR("worker:: Worker loop is nullptr");
|
|
return;
|
|
}
|
|
|
|
// 2. add some preparation for the worker
|
|
if (worker->PrepareForWorkerInstance()) {
|
|
uv_async_init(loop, &worker->workerOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::WorkerOnMessage));
|
|
worker->UpdateWorkerState(RUNNING);
|
|
// in order to invoke worker send before subThread start
|
|
uv_async_send(&worker->workerOnMessageSignal_);
|
|
// 3. start worker loop
|
|
worker->Loop();
|
|
} else {
|
|
HILOG_ERROR("worker:: worker PrepareForWorkerInstance failure");
|
|
worker->UpdateWorkerState(TERMINATED);
|
|
}
|
|
worker->ReleaseWorkerThreadContent();
|
|
std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
|
|
if (worker->HostIsStop()) {
|
|
Helper::CloseHelp::DeletePointer(worker, false);
|
|
} else {
|
|
worker->PublishWorkerOverSignal();
|
|
}
|
|
}
|
|
|
|
void Worker::HostOnMessage(const uv_async_t* req)
|
|
{
|
|
Worker* worker = Helper::DereferenceHelp::DereferenceOf(&Worker::hostOnMessageSignal_, req);
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker::worker is null");
|
|
return;
|
|
}
|
|
worker->HostOnMessageInner();
|
|
}
|
|
|
|
void Worker::HostOnErrorInner()
|
|
{
|
|
if (hostEnv_ == nullptr || HostIsStop()) {
|
|
HILOG_ERROR("worker:: host thread maybe is over");
|
|
return;
|
|
}
|
|
napi_value obj = Helper::NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
|
|
napi_value callback = Helper::NapiHelper::GetNameProperty(hostEnv_, obj, "onerror");
|
|
bool isCallable = Helper::NapiHelper::IsCallable(hostEnv_, callback);
|
|
if (!isCallable) {
|
|
HILOG_ERROR("worker:: worker onerror is not Callable");
|
|
return;
|
|
}
|
|
MessageDataType data;
|
|
while (errorQueue_.DeQueue(&data)) {
|
|
napi_value result = nullptr;
|
|
napi_deserialize(hostEnv_, data, &result);
|
|
|
|
napi_value argv[1] = { result };
|
|
napi_value callbackResult = nullptr;
|
|
napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult);
|
|
|
|
// handle listeners
|
|
HandleEventListeners(hostEnv_, obj, 1, argv, "error");
|
|
}
|
|
}
|
|
|
|
void Worker::HostOnError(const uv_async_t* req)
|
|
{
|
|
Worker* worker = Helper::DereferenceHelp::DereferenceOf(&Worker::hostOnErrorSignal_, req);
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker::worker is null");
|
|
return;
|
|
}
|
|
worker->HostOnErrorInner();
|
|
worker->TerminateInner();
|
|
}
|
|
|
|
void Worker::WorkerOnMessage(const uv_async_t* req)
|
|
{
|
|
Worker* worker = Helper::DereferenceHelp::DereferenceOf(&Worker::workerOnMessageSignal_, req);
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker::worker is null");
|
|
return;
|
|
}
|
|
worker->WorkerOnMessageInner();
|
|
}
|
|
|
|
void Worker::CloseHostCallback() const
|
|
{
|
|
napi_value exitValue = nullptr;
|
|
napi_create_int32(hostEnv_, 1, &exitValue);
|
|
napi_value argv[1] = { exitValue };
|
|
CallHostFunction(1, argv, "onexit");
|
|
Helper::CloseHelp::DeletePointer(this, false);
|
|
}
|
|
|
|
void Worker::HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type)
|
|
{
|
|
std::string listener(type);
|
|
auto iter = eventListeners_.find(listener);
|
|
if (iter == eventListeners_.end()) {
|
|
HILOG_INFO("worker:: there is no listener for type %{public}s", type);
|
|
return;
|
|
}
|
|
|
|
std::list<WorkerListener*>& listeners = iter->second;
|
|
std::list<WorkerListener*>::iterator it = listeners.begin();
|
|
while (it != listeners.end()) {
|
|
WorkerListener* data = *it++;
|
|
napi_value callbackObj = Helper::NapiHelper::GetReferenceValue(env, data->callback_);
|
|
napi_value callbackResult = nullptr;
|
|
napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult);
|
|
if (!data->NextIsAvailable()) {
|
|
listeners.remove(data);
|
|
Helper::CloseHelp::DeletePointer(data, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Worker::HostOnMessageInner()
|
|
{
|
|
if (hostEnv_ == nullptr || HostIsStop()) {
|
|
HILOG_ERROR("worker:: host thread maybe is over");
|
|
return;
|
|
}
|
|
napi_value obj = Helper::NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
|
|
napi_value callback = Helper::NapiHelper::GetNameProperty(hostEnv_, obj, "onmessage");
|
|
bool isCallable = Helper::NapiHelper::IsCallable(hostEnv_, callback);
|
|
|
|
MessageDataType data = nullptr;
|
|
while (hostMessageQueue_.DeQueue(&data)) {
|
|
// receive close signal.
|
|
if (data == nullptr) {
|
|
HILOG_INFO("worker:: worker received close signal");
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&hostOnMessageSignal_), nullptr);
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&hostOnErrorSignal_), nullptr);
|
|
CloseHostCallback();
|
|
return;
|
|
}
|
|
if (!isCallable) {
|
|
// onmessage is not func, no need to continue
|
|
HILOG_ERROR("worker:: worker onmessage is not a callable");
|
|
continue;
|
|
}
|
|
// handle data, call worker onMessage function to handle.
|
|
napi_value result = nullptr;
|
|
napi_status status = napi_deserialize(hostEnv_, data, &result);
|
|
if (status != napi_ok || result == nullptr) {
|
|
HostOnMessageErrorInner();
|
|
continue;
|
|
}
|
|
napi_value event = nullptr;
|
|
napi_create_object(hostEnv_, &event);
|
|
napi_set_named_property(hostEnv_, event, "data", result);
|
|
napi_value argv[1] = { event };
|
|
napi_value callbackResult = nullptr;
|
|
napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult);
|
|
// handle listeners.
|
|
HandleEventListeners(hostEnv_, obj, 1, argv, "message");
|
|
}
|
|
}
|
|
|
|
void Worker::TerminateWorker()
|
|
{
|
|
// when there is no active handle, worker loop will stop automatic.
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&workerOnMessageSignal_), nullptr);
|
|
CloseWorkerCallback();
|
|
uv_loop_t* loop = GetWorkerLoop();
|
|
if (loop != nullptr) {
|
|
Plugin::Timer::ClearEnvironmentTimer(workerEnv_);
|
|
uv_stop(loop);
|
|
}
|
|
UpdateWorkerState(TERMINATED);
|
|
}
|
|
|
|
void Worker::HandleException()
|
|
{
|
|
// obj.message, obj.filename, obj.lineno, obj.colno
|
|
napi_value exception = nullptr;
|
|
napi_create_object(workerEnv_, &exception);
|
|
|
|
napi_get_exception_info_for_worker(workerEnv_, exception);
|
|
|
|
// add obj.filename
|
|
napi_value filenameValue = nullptr;
|
|
napi_create_string_utf8(workerEnv_, script_.c_str(), script_.length(), &filenameValue);
|
|
napi_set_named_property(workerEnv_, exception, "filename", filenameValue);
|
|
|
|
// WorkerGlobalScope onerror
|
|
WorkerOnErrorInner(exception);
|
|
|
|
if (hostEnv_ != nullptr) {
|
|
napi_value data = nullptr;
|
|
napi_serialize(workerEnv_, exception, Helper::NapiHelper::GetUndefinedValue(workerEnv_), &data);
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
|
|
if (!HostIsStop()) {
|
|
errorQueue_.EnQueue(data);
|
|
uv_async_send(&hostOnErrorSignal_);
|
|
}
|
|
}
|
|
} else {
|
|
HILOG_ERROR("worker:: host engine is nullptr.");
|
|
}
|
|
}
|
|
|
|
void Worker::WorkerOnMessageInner()
|
|
{
|
|
if (IsTerminated()) {
|
|
return;
|
|
}
|
|
MessageDataType data = nullptr;
|
|
while (!IsTerminated() && workerMessageQueue_.DeQueue(&data)) {
|
|
if (data == nullptr) {
|
|
HILOG_INFO("worker:: worker reveive terminate signal");
|
|
TerminateWorker();
|
|
return;
|
|
}
|
|
napi_value result = nullptr;
|
|
napi_status status = napi_deserialize(workerEnv_, data, &result);
|
|
if (status != napi_ok || result == nullptr) {
|
|
WorkerOnMessageErrorInner();
|
|
continue;
|
|
}
|
|
|
|
napi_value event = nullptr;
|
|
napi_create_object(workerEnv_, &event);
|
|
napi_set_named_property(workerEnv_, event, "data", result);
|
|
napi_value argv[1] = { event };
|
|
bool callFeedback = CallWorkerFunction(1, argv, "onmessage", true);
|
|
if (!callFeedback) {
|
|
HILOG_ERROR("worker:: call WorkerGlobalScope onmessage error");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Worker::HostOnMessageErrorInner()
|
|
{
|
|
if (hostEnv_ == nullptr || HostIsStop()) {
|
|
HILOG_ERROR("worker:: host thread maybe is over");
|
|
return;
|
|
}
|
|
napi_value obj = Helper::NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
|
|
CallHostFunction(0, nullptr, "onmessageerror");
|
|
// handle listeners
|
|
HandleEventListeners(hostEnv_, obj, 0, nullptr, "messageerror");
|
|
}
|
|
|
|
void Worker::WorkerOnMessageErrorInner()
|
|
{
|
|
CallWorkerFunction(0, nullptr, "onmessageerror", true);
|
|
}
|
|
|
|
napi_value Worker::PostMessage(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < 1) {
|
|
napi_throw_error(env, nullptr, "Worker param count must be more than 1 with postMessage");
|
|
return nullptr;
|
|
}
|
|
napi_value* argv = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(argv, true);
|
|
napi_value thisVar = nullptr;
|
|
napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr);
|
|
Worker* worker = nullptr;
|
|
napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
|
|
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when PostMessage, maybe worker is terminated");
|
|
return nullptr;
|
|
}
|
|
|
|
if (worker->IsTerminated() || worker->IsTerminating()) {
|
|
HILOG_INFO("worker:: worker not in running state");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_value data = nullptr;
|
|
napi_status serializeStatus = napi_ok;
|
|
if (argc >= WORKERPARAMNUM) {
|
|
if (!Helper::NapiHelper::IsArray(argv[1])) {
|
|
napi_throw_error(env, nullptr, "Transfer list must be an Array");
|
|
return nullptr;
|
|
}
|
|
serializeStatus = napi_serialize(env, argv[0], argv[1], &data);
|
|
} else {
|
|
serializeStatus = napi_serialize(env, argv[0], Helper::NapiHelper::GetUndefinedValue(env), &data);
|
|
}
|
|
if (serializeStatus != napi_ok || data == nullptr) {
|
|
worker->HostOnMessageErrorInner();
|
|
return nullptr;
|
|
}
|
|
worker->PostMessageInner(data);
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
napi_value Worker::PostMessageToHost(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < 1) {
|
|
napi_throw_error(env, nullptr, "Worker param count must be more than 1 with new");
|
|
return nullptr;
|
|
}
|
|
napi_value* argv = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(argv, true);
|
|
Worker* worker = nullptr;
|
|
napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, reinterpret_cast<void**>(&worker));
|
|
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!worker->IsRunning()) {
|
|
// if worker is not running, don't send any message to host thread
|
|
HILOG_INFO("worker:: when post message to host occur worker is not in running.");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_value data = nullptr;
|
|
napi_status serializeStatus = napi_ok;
|
|
if (argc >= WORKERPARAMNUM) {
|
|
if (!Helper::NapiHelper::IsArray(argv[1])) {
|
|
napi_throw_error(env, nullptr, "Transfer list must be an Array");
|
|
return nullptr;
|
|
}
|
|
serializeStatus = napi_serialize(env, argv[0], argv[1], &data);
|
|
} else {
|
|
serializeStatus = napi_serialize(env, argv[0], Helper::NapiHelper::GetUndefinedValue(env), &data);
|
|
}
|
|
|
|
if (serializeStatus != napi_ok || data == nullptr) {
|
|
worker->WorkerOnMessageErrorInner();
|
|
return nullptr;
|
|
}
|
|
worker->PostMessageToHostInner(data);
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
void Worker::PostMessageToHostInner(MessageDataType data)
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
|
|
if (hostEnv_ != nullptr && !HostIsStop()) {
|
|
hostMessageQueue_.EnQueue(data);
|
|
uv_async_send(&hostOnMessageSignal_);
|
|
} else {
|
|
HILOG_ERROR("worker:: worker host engine is nullptr.");
|
|
}
|
|
}
|
|
|
|
void Worker::PostMessageInner(MessageDataType data)
|
|
{
|
|
if (IsTerminated()) {
|
|
HILOG_INFO("worker:: worker has been terminated.");
|
|
return;
|
|
}
|
|
workerMessageQueue_.EnQueue(data);
|
|
if (uv_is_active((uv_handle_t*)&workerOnMessageSignal_)) {
|
|
uv_async_send(&workerOnMessageSignal_);
|
|
}
|
|
}
|
|
|
|
napi_value Worker::Terminate(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
napi_value thisVar = nullptr;
|
|
napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
|
|
Worker* worker = nullptr;
|
|
napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when Terminate, maybe worker is terminated");
|
|
return nullptr;
|
|
}
|
|
if (worker->IsTerminated() || worker->IsTerminating()) {
|
|
HILOG_INFO("worker:: worker is not in running");
|
|
return nullptr;
|
|
}
|
|
worker->TerminateInner();
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
void Worker::TerminateInner()
|
|
{
|
|
if (IsTerminated() || IsTerminating()) {
|
|
HILOG_INFO("worker:: worker is not in running");
|
|
return;
|
|
}
|
|
// 1. Update State
|
|
UpdateWorkerState(TERMINATEING);
|
|
// 2. send null signal
|
|
PostMessageInner(NULL);
|
|
}
|
|
|
|
Worker::~Worker()
|
|
{
|
|
if (!HostIsStop()) {
|
|
ReleaseHostThreadContent();
|
|
}
|
|
RemoveAllListenerInner();
|
|
}
|
|
|
|
napi_value Worker::CancelTask(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
napi_value thisVar = nullptr;
|
|
napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
|
|
Worker* worker = nullptr;
|
|
napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated");
|
|
return nullptr;
|
|
}
|
|
|
|
if (worker->IsTerminated() || worker->IsTerminating()) {
|
|
HILOG_INFO("worker:: worker is not in running");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!worker->ClearWorkerTasks()) {
|
|
HILOG_ERROR("worker:: clear worker task error");
|
|
}
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
napi_value Worker::ParentPortCancelTask(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
Worker* worker = nullptr;
|
|
napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker));
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated");
|
|
return nullptr;
|
|
}
|
|
|
|
if (worker->IsTerminated() || worker->IsTerminating()) {
|
|
HILOG_INFO("worker:: worker is not in running");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!worker->ClearWorkerTasks()) {
|
|
HILOG_ERROR("worker:: clear worker task error");
|
|
}
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
napi_value Worker::WorkerConstructor(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
// check argv count
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < 1) {
|
|
napi_throw_error(env, nullptr, "Worker param count must be more than 1 with new");
|
|
return nullptr;
|
|
}
|
|
|
|
// check 1st param is string
|
|
napi_value thisVar = nullptr;
|
|
void* data = nullptr;
|
|
napi_value* args = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(args, true);
|
|
napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
|
|
if (!Helper::NapiHelper::IsString(args[0])) {
|
|
napi_throw_error(env, nullptr, "Worker 1st param must be string with new");
|
|
return nullptr;
|
|
}
|
|
Worker* worker = nullptr;
|
|
{
|
|
std::lock_guard<std::mutex> lock(g_workersMutex);
|
|
if (g_workers.size() >= MAXWORKERS) {
|
|
napi_throw_error(env, nullptr, "Too many workers, the number of workers exceeds the maximum.");
|
|
return nullptr;
|
|
}
|
|
|
|
// 2. new worker instance
|
|
worker = new Worker(env, nullptr);
|
|
if (worker == nullptr) {
|
|
napi_throw_error(env, nullptr, "create worker error");
|
|
return nullptr;
|
|
}
|
|
g_workers.push_back(worker);
|
|
}
|
|
|
|
if (argc > 1 && Helper::NapiHelper::IsObject(args[1])) {
|
|
napi_value nameValue = Helper::NapiHelper::GetNameProperty(env, args[1], "name");
|
|
if (Helper::NapiHelper::IsString(nameValue)) {
|
|
char* nameStr = Helper::NapiHelper::GetString(env, nameValue);
|
|
if (nameStr == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker name create error, please check.");
|
|
return nullptr;
|
|
}
|
|
worker->name_ = std::string(nameStr);
|
|
Helper::CloseHelp::DeletePointer(nameStr, true);
|
|
}
|
|
|
|
napi_value typeValue = Helper::NapiHelper::GetNameProperty(env, args[1], "type");
|
|
if (Helper::NapiHelper::IsString(typeValue)) {
|
|
char* typeStr = Helper::NapiHelper::GetString(env, typeValue);
|
|
if (typeStr == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker type create error, please check.");
|
|
return nullptr;
|
|
}
|
|
if (strcmp("classic", typeStr) == 0) {
|
|
worker->SetScriptMode(CLASSIC);
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
} else {
|
|
napi_throw_error(env, nullptr, "unsupport module");
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
Helper::CloseHelp::DeletePointer(worker, false);
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. execute in thread
|
|
char* script = Helper::NapiHelper::GetString(env, args[0]);
|
|
if (script == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker script create error, please check.");
|
|
return nullptr;
|
|
}
|
|
napi_wrap(
|
|
env, thisVar, worker,
|
|
[](napi_env env, void* data, void* hint) {
|
|
Worker* worker = reinterpret_cast<Worker*>(data);
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_);
|
|
if (worker->UpdateHostState(INACTIVE)) {
|
|
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&worker->hostOnMessageSignal_))) {
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&worker->hostOnMessageSignal_), nullptr);
|
|
}
|
|
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&worker->hostOnErrorSignal_))) {
|
|
uv_close(reinterpret_cast<uv_handle_t*>(&worker->hostOnErrorSignal_), nullptr);
|
|
}
|
|
worker->ReleaseHostThreadContent();
|
|
}
|
|
if (!worker->IsRunning()) {
|
|
HILOG_INFO("worker:: worker is not in running");
|
|
return;
|
|
}
|
|
worker->TerminateInner();
|
|
}
|
|
},
|
|
nullptr, nullptr);
|
|
napi_create_reference(env, thisVar, 1, &worker->workerRef_);
|
|
worker->StartExecuteInThread(env, script);
|
|
return thisVar;
|
|
}
|
|
|
|
napi_value Worker::AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < WORKERPARAMNUM) {
|
|
napi_throw_error(env, nullptr, "Worker param count must be more than WORKPARAMNUM with on");
|
|
return nullptr;
|
|
}
|
|
// check 1st param is string
|
|
napi_value thisVar = nullptr;
|
|
void* data = nullptr;
|
|
napi_value* args = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(args, true);
|
|
napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
|
|
if (!Helper::NapiHelper::IsString(args[0])) {
|
|
napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
|
|
return nullptr;
|
|
}
|
|
if (!Helper::NapiHelper::IsCallable(env, args[1])) {
|
|
napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
|
|
return nullptr;
|
|
}
|
|
Worker* worker = nullptr;
|
|
napi_unwrap(env, thisVar, (void**)&worker);
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when addListener, maybe worker is terminated");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_ref callback = Helper::NapiHelper::CreateReference(env, args[1], 1);
|
|
auto listener = new WorkerListener(env, callback, mode);
|
|
if (mode == ONCE && argc > WORKERPARAMNUM) {
|
|
if (Helper::NapiHelper::IsObject(args[WORKERPARAMNUM])) {
|
|
napi_value onceValue = Helper::NapiHelper::GetNameProperty(env, args[WORKERPARAMNUM], "once");
|
|
bool isOnce = Helper::NapiHelper::GetBooleanValue(env, onceValue);
|
|
if (!isOnce) {
|
|
listener->SetMode(PERMANENT);
|
|
}
|
|
}
|
|
}
|
|
char* typeStr = Helper::NapiHelper::GetString(env, args[0]);
|
|
if (typeStr == nullptr) {
|
|
Helper::CloseHelp::DeletePointer(listener, false);
|
|
napi_throw_error(env, nullptr, "worker listener type create error, please check.");
|
|
return nullptr;
|
|
}
|
|
worker->AddListenerInner(env, typeStr, listener);
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
bool Worker::WorkerListener::operator==(const WorkerListener& listener) const
|
|
{
|
|
napi_value obj = Helper::NapiHelper::GetReferenceValue(listener.env_, listener.callback_);
|
|
napi_value compareObj = Helper::NapiHelper::GetReferenceValue(env_, callback_);
|
|
// the env of listener and cmp listener must be same env because of Synchronization method
|
|
return Helper::NapiHelper::StrictEqual(env_, compareObj, obj);
|
|
}
|
|
|
|
void Worker::AddListenerInner(napi_env env, const char* type, const WorkerListener* listener)
|
|
{
|
|
std::string typestr(type);
|
|
auto iter = eventListeners_.find(typestr);
|
|
if (iter == eventListeners_.end()) {
|
|
std::list<WorkerListener*> listeners;
|
|
listeners.emplace_back(const_cast<WorkerListener*>(listener));
|
|
eventListeners_[typestr] = listeners;
|
|
} else {
|
|
std::list<WorkerListener*>& listenerList = iter->second;
|
|
std::list<WorkerListener*>::iterator it = std::find_if(
|
|
listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_));
|
|
if (it != listenerList.end()) {
|
|
return;
|
|
}
|
|
listenerList.emplace_back(const_cast<WorkerListener*>(listener));
|
|
}
|
|
}
|
|
|
|
void Worker::RemoveListenerInner(napi_env env, const char* type, napi_ref callback)
|
|
{
|
|
std::string typestr(type);
|
|
auto iter = eventListeners_.find(typestr);
|
|
if (iter == eventListeners_.end()) {
|
|
return;
|
|
}
|
|
std::list<WorkerListener*>& listenerList = iter->second;
|
|
if (callback != nullptr) {
|
|
std::list<WorkerListener*>::iterator it =
|
|
std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback));
|
|
if (it != listenerList.end()) {
|
|
Helper::CloseHelp::DeletePointer(*it, false);
|
|
listenerList.erase(it);
|
|
}
|
|
} else {
|
|
for (auto it = listenerList.begin(); it != listenerList.end(); it++) {
|
|
Helper::CloseHelp::DeletePointer(*it, false);
|
|
}
|
|
eventListeners_.erase(typestr);
|
|
}
|
|
}
|
|
|
|
napi_value Worker::On(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
return AddListener(env, cbinfo, PERMANENT);
|
|
}
|
|
|
|
napi_value Worker::Once(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
return AddListener(env, cbinfo, ONCE);
|
|
}
|
|
|
|
napi_value Worker::RemoveListener(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < 1) {
|
|
napi_throw_error(env, nullptr, "Worker param count must be more than 2 with on");
|
|
return nullptr;
|
|
}
|
|
// check 1st param is string
|
|
napi_value thisVar = nullptr;
|
|
void* data = nullptr;
|
|
napi_value* args = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(args, true);
|
|
napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
|
|
if (!Helper::NapiHelper::IsString(args[0])) {
|
|
napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
|
|
return nullptr;
|
|
}
|
|
|
|
Worker* worker = nullptr;
|
|
napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when RemoveListener, maybe worker is terminated");
|
|
return nullptr;
|
|
}
|
|
|
|
if (argc > 1 && !Helper::NapiHelper::IsCallable(env, args[1])) {
|
|
napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
|
|
return nullptr;
|
|
}
|
|
|
|
char* typeStr = Helper::NapiHelper::GetString(env, args[0]);
|
|
if (typeStr == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker listener type create error, please check.");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_ref callback = nullptr;
|
|
if (argc > 1 && Helper::NapiHelper::IsCallable(env, args[1])) {
|
|
napi_create_reference(env, args[1], 1, &callback);
|
|
}
|
|
worker->RemoveListenerInner(env, typeStr, callback);
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
Helper::NapiHelper::DeleteReference(env, callback);
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
napi_value Worker::Off(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
return RemoveListener(env, cbinfo);
|
|
}
|
|
|
|
napi_value Worker::AddEventListener(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
return AddListener(env, cbinfo, PERMANENT);
|
|
}
|
|
|
|
napi_value Worker::DispatchEvent(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < 1) {
|
|
napi_throw_error(env, nullptr, "worker:: DispatchEvent param count must be more than 1");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
// check 1st param is string
|
|
napi_value thisVar = nullptr;
|
|
void* data = nullptr;
|
|
napi_value* args = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(args, true);
|
|
napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data);
|
|
|
|
if (!Helper::NapiHelper::IsObject(args[0])) {
|
|
napi_throw_error(env, nullptr, "worker DispatchEvent 1st param must be Event");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
Worker* worker = nullptr;
|
|
napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when DispatchEvent, maybe worker is terminated");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
napi_value typeValue = Helper::NapiHelper::GetNameProperty(env, args[0], "type");
|
|
if (!Helper::NapiHelper::IsString(typeValue)) {
|
|
napi_throw_error(env, nullptr, "worker event type must be string");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
napi_value obj = Helper::NapiHelper::GetReferenceValue(env, worker->workerRef_);
|
|
napi_value argv[1] = { args[0] };
|
|
|
|
char* typeStr = Helper::NapiHelper::GetString(env, typeValue);
|
|
if (typeStr == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker listener type create error, please check.");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
if (strcmp(typeStr, "error") == 0) {
|
|
CallWorkCallback(env, obj, 1, argv, "onerror");
|
|
} else if (strcmp(typeStr, "messageerror") == 0) {
|
|
CallWorkCallback(env, obj, 1, argv, "onmessageerror");
|
|
} else if (strcmp(typeStr, "message") == 0) {
|
|
CallWorkCallback(env, obj, 1, argv, "onmessage");
|
|
}
|
|
|
|
worker->HandleEventListeners(env, obj, 1, argv, typeStr);
|
|
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
return Helper::NapiHelper::CreateBooleanValue(env, true);
|
|
}
|
|
|
|
napi_value Worker::RemoveEventListener(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
return RemoveListener(env, cbinfo);
|
|
}
|
|
|
|
void Worker::RemoveAllListenerInner()
|
|
{
|
|
for (auto iter = eventListeners_.begin(); iter != eventListeners_.end(); iter++) {
|
|
std::list<WorkerListener*>& listeners = iter->second;
|
|
for (auto item = listeners.begin(); item != listeners.end(); item++) {
|
|
WorkerListener* listener = *item;
|
|
Helper::CloseHelp::DeletePointer(listener, false);
|
|
}
|
|
}
|
|
eventListeners_.clear();
|
|
}
|
|
|
|
napi_value Worker::RemoveAllListener(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
napi_value thisVar = nullptr;
|
|
napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr);
|
|
Worker* worker = nullptr;
|
|
napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker));
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: worker is nullptr when RemoveAllListener, maybe worker is terminated");
|
|
return nullptr;
|
|
}
|
|
|
|
worker->RemoveAllListenerInner();
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
napi_value Worker::InitWorker(napi_env env, napi_value exports)
|
|
{
|
|
NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
|
|
const char className[] = "Worker";
|
|
napi_property_descriptor properties[] = {
|
|
DECLARE_NAPI_FUNCTION("postMessage", PostMessage),
|
|
DECLARE_NAPI_FUNCTION("terminate", Terminate),
|
|
DECLARE_NAPI_FUNCTION("on", On),
|
|
DECLARE_NAPI_FUNCTION("once", Once),
|
|
DECLARE_NAPI_FUNCTION("off", Off),
|
|
DECLARE_NAPI_FUNCTION("addEventListener", AddEventListener),
|
|
DECLARE_NAPI_FUNCTION("dispatchEvent", DispatchEvent),
|
|
DECLARE_NAPI_FUNCTION("removeEventListener", RemoveEventListener),
|
|
DECLARE_NAPI_FUNCTION("removeAllListener", RemoveAllListener),
|
|
DECLARE_NAPI_FUNCTION("cancelTasks", CancelTask),
|
|
};
|
|
napi_value workerClazz = nullptr;
|
|
napi_define_class(env, className, sizeof(className), Worker::WorkerConstructor, nullptr,
|
|
sizeof(properties) / sizeof(properties[0]), properties, &workerClazz);
|
|
napi_set_named_property(env, exports, "Worker", workerClazz);
|
|
|
|
if (!engine->IsMainThread()) {
|
|
Worker* worker = nullptr;
|
|
for (auto item = g_workers.begin(); item != g_workers.end(); item++) {
|
|
if ((*item)->IsSameWorkerEnv(env)) {
|
|
worker = *item;
|
|
}
|
|
}
|
|
if (worker == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker:: worker is null");
|
|
return exports;
|
|
}
|
|
|
|
napi_property_descriptor properties[] = {
|
|
DECLARE_NAPI_FUNCTION_WITH_DATA("postMessage", PostMessageToHost, worker),
|
|
DECLARE_NAPI_FUNCTION_WITH_DATA("close", CloseWorker, worker),
|
|
DECLARE_NAPI_FUNCTION_WITH_DATA("cancelTasks", ParentPortCancelTask, worker),
|
|
DECLARE_NAPI_FUNCTION_WITH_DATA("addEventListener", ParentPortAddEventListener, worker),
|
|
DECLARE_NAPI_FUNCTION_WITH_DATA("dispatchEvent", ParentPortDispatchEvent, worker),
|
|
DECLARE_NAPI_FUNCTION_WITH_DATA("removeEventListener", ParentPortRemoveEventListener, worker),
|
|
DECLARE_NAPI_FUNCTION_WITH_DATA("removeAllListener", ParentPortRemoveAllListener, worker),
|
|
};
|
|
napi_value parentPortObj = nullptr;
|
|
napi_create_object(env, &parentPortObj);
|
|
napi_define_properties(env, parentPortObj, sizeof(properties) / sizeof(properties[0]), properties);
|
|
|
|
// 5. register worker name in DedicatedWorkerGlobalScope
|
|
std::string workerName = worker->GetName();
|
|
if (!workerName.empty()) {
|
|
napi_value nameValue = nullptr;
|
|
napi_create_string_utf8(env, workerName.c_str(), workerName.length(), &nameValue);
|
|
napi_set_named_property(env, parentPortObj, "name", nameValue);
|
|
}
|
|
napi_set_named_property(env, exports, "parentPort", parentPortObj);
|
|
|
|
// register worker parentPort.
|
|
napi_create_reference(env, parentPortObj, 1, &worker->parentPort_);
|
|
}
|
|
return exports;
|
|
}
|
|
|
|
void Worker::WorkerOnErrorInner(napi_value error)
|
|
{
|
|
napi_value argv[1] = { error };
|
|
CallWorkerFunction(1, argv, "onerror", false);
|
|
}
|
|
|
|
bool Worker::CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch)
|
|
{
|
|
if (workerEnv_ == nullptr) {
|
|
return false;
|
|
}
|
|
napi_value callback = Helper::NapiHelper::GetNamePropertyInParentPort(workerEnv_, parentPort_, methodName);
|
|
bool isCallable = Helper::NapiHelper::IsCallable(workerEnv_, callback);
|
|
if (!isCallable) {
|
|
HILOG_ERROR("worker:: WorkerGlobalScope %{public}s is not Callable", methodName);
|
|
return false;
|
|
}
|
|
napi_value undefinedValue = Helper::NapiHelper::GetUndefinedValue(workerEnv_);
|
|
napi_value callbackResult = nullptr;
|
|
napi_call_function(workerEnv_, undefinedValue, callback, argc, argv, &callbackResult);
|
|
if (tryCatch && callbackResult == nullptr) {
|
|
// handle exception
|
|
HandleException();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Worker::CloseWorkerCallback()
|
|
{
|
|
CallWorkerFunction(0, nullptr, "onclose", true);
|
|
// off worker inited environment
|
|
{
|
|
std::lock_guard<std::recursive_mutex> lock(liveStatusLock_);
|
|
if (HostIsStop()) {
|
|
return;
|
|
}
|
|
auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_);
|
|
if (!hostEngine->CallOffWorkerFunc(reinterpret_cast<NativeEngine*>(workerEnv_))) {
|
|
HILOG_ERROR("worker:: CallOffWorkerFunc error");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Worker::CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const
|
|
{
|
|
if (hostEnv_ == nullptr || HostIsStop()) {
|
|
HILOG_ERROR("worker:: host thread maybe is over");
|
|
return;
|
|
}
|
|
napi_value obj = Helper::NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
|
|
napi_value callback = Helper::NapiHelper::GetNameProperty(hostEnv_, obj, methodName);
|
|
bool isCallable = Helper::NapiHelper::IsCallable(hostEnv_, callback);
|
|
if (!isCallable) {
|
|
HILOG_ERROR("worker:: worker %{public}s is not Callable", methodName);
|
|
return;
|
|
}
|
|
napi_value callbackResult = nullptr;
|
|
napi_call_function(hostEnv_, obj, callback, argc, argv, &callbackResult);
|
|
}
|
|
|
|
void Worker::ReleaseWorkerThreadContent()
|
|
{
|
|
// 1. remove worker instance count
|
|
{
|
|
std::lock_guard<std::mutex> lock(g_workersMutex);
|
|
std::list<Worker*>::iterator it = std::find(g_workers.begin(), g_workers.end(), this);
|
|
if (it != g_workers.end()) {
|
|
g_workers.erase(it);
|
|
}
|
|
}
|
|
|
|
ParentPortRemoveAllListenerInner();
|
|
|
|
// 2. delete worker's parentPort
|
|
Helper::NapiHelper::DeleteReference(workerEnv_, parentPort_);
|
|
parentPort_ = nullptr;
|
|
|
|
// 3. clear message send to worker thread
|
|
workerMessageQueue_.Clear(workerEnv_);
|
|
// 4. delete NativeEngine created in worker thread
|
|
auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_);
|
|
workerEngine->CloseAsyncWork();
|
|
Helper::CloseHelp::DeletePointer(reinterpret_cast<NativeEngine*>(workerEnv_), false);
|
|
workerEnv_ = nullptr;
|
|
}
|
|
|
|
void Worker::ReleaseHostThreadContent()
|
|
{
|
|
// 1. clear message send to host thread
|
|
hostMessageQueue_.Clear(hostEnv_);
|
|
// 2. clear error queue send to host thread
|
|
errorQueue_.Clear(hostEnv_);
|
|
if (!HostIsStop()) {
|
|
// 3. set thisVar's nativepointer be null
|
|
napi_value thisVar = Helper::NapiHelper::GetReferenceValue(hostEnv_, workerRef_);
|
|
Worker* worker = nullptr;
|
|
napi_remove_wrap(hostEnv_, thisVar, reinterpret_cast<void**>(&worker));
|
|
// 4. set workerRef_ be null
|
|
Helper::NapiHelper::DeleteReference(hostEnv_, workerRef_);
|
|
}
|
|
hostEnv_ = nullptr;
|
|
workerRef_ = nullptr;
|
|
}
|
|
|
|
napi_value Worker::ParentPortAddEventListener(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < WORKERPARAMNUM) {
|
|
napi_throw_error(env, nullptr, "Worker param count must be more than WORKPARAMNUM with on");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_value* args = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(args, true);
|
|
Worker* worker = nullptr;
|
|
napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
|
|
|
|
if (!Helper::NapiHelper::IsString(args[0])) {
|
|
napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!Helper::NapiHelper::IsCallable(env, args[1])) {
|
|
napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
|
|
return nullptr;
|
|
}
|
|
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!worker->IsRunning()) {
|
|
// if worker is not running, don't send any message to host thread
|
|
HILOG_INFO("worker:: when post message to host occur worker is not in running.");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_ref callback = Helper::NapiHelper::CreateReference(env, args[1], 1);
|
|
auto listener = new WorkerListener(env, callback, PERMANENT);
|
|
if (argc > WORKERPARAMNUM && Helper::NapiHelper::IsObject(args[WORKERPARAMNUM])) {
|
|
napi_value onceValue = Helper::NapiHelper::GetNameProperty(env, args[WORKERPARAMNUM], "once");
|
|
bool isOnce = Helper::NapiHelper::GetBooleanValue(env, onceValue);
|
|
if (isOnce) {
|
|
listener->SetMode(ONCE);
|
|
}
|
|
}
|
|
char* typeStr = Helper::NapiHelper::GetString(env, args[0]);
|
|
if (typeStr == nullptr) {
|
|
Helper::CloseHelp::DeletePointer(listener, false);
|
|
napi_throw_error(env, nullptr, "worker listener type create error, please check.");
|
|
return nullptr;
|
|
}
|
|
worker->ParentPortAddListenerInner(env, typeStr, listener);
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
napi_value Worker::ParentPortRemoveAllListener(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
Worker* worker = nullptr;
|
|
napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker));
|
|
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!worker->IsRunning()) {
|
|
// if worker is not running, don't send any message to host thread
|
|
HILOG_INFO("worker:: when post message to host occur worker is not in running.");
|
|
return nullptr;
|
|
}
|
|
|
|
worker->ParentPortRemoveAllListenerInner();
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
napi_value Worker::ParentPortDispatchEvent(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < 1) {
|
|
napi_throw_error(env, nullptr, "worker:: DispatchEvent param count must be more than 1");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
napi_value* args = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(args, true);
|
|
Worker* worker = nullptr;
|
|
napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
|
|
|
|
if (!Helper::NapiHelper::IsObject(args[0])) {
|
|
napi_throw_error(env, nullptr, "worker DispatchEvent 1st param must be Event");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
napi_value typeValue = Helper::NapiHelper::GetNameProperty(env, args[0], "type");
|
|
if (!Helper::NapiHelper::IsString(typeValue)) {
|
|
napi_throw_error(env, nullptr, "worker event type must be string");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
if (!worker->IsRunning()) {
|
|
// if worker is not running, don't send any message to host thread
|
|
HILOG_INFO("worker:: when post message to host occur worker is not in running.");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
napi_value argv[1] = { args[0] };
|
|
char* typeStr = Helper::NapiHelper::GetString(env, typeValue);
|
|
if (typeStr == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker listener type create error, please check.");
|
|
return Helper::NapiHelper::CreateBooleanValue(env, false);
|
|
}
|
|
|
|
napi_value obj = Helper::NapiHelper::GetReferenceValue(env, worker->parentPort_);
|
|
|
|
if (strcmp(typeStr, "error") == 0) {
|
|
CallWorkCallback(env, obj, 1, argv, "onerror");
|
|
} else if (strcmp(typeStr, "messageerror") == 0) {
|
|
CallWorkCallback(env, obj, 1, argv, "onmessageerror");
|
|
} else if (strcmp(typeStr, "message") == 0) {
|
|
CallWorkCallback(env, obj, 1, argv, "onmessage");
|
|
}
|
|
|
|
worker->ParentPortHandleEventListeners(env, obj, 1, argv, typeStr);
|
|
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
return Helper::NapiHelper::CreateBooleanValue(env, true);
|
|
}
|
|
|
|
napi_value Worker::ParentPortRemoveEventListener(napi_env env, napi_callback_info cbinfo)
|
|
{
|
|
size_t argc = Helper::NapiHelper::GetCallbackInfoArgc(env, cbinfo);
|
|
if (argc < 1) {
|
|
napi_throw_error(env, nullptr, "Worker param count must be more than 2 with on");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_value* args = new napi_value[argc];
|
|
Helper::ObjectScope<napi_value> scope(args, true);
|
|
Worker* worker = nullptr;
|
|
napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker));
|
|
|
|
if (!Helper::NapiHelper::IsString(args[0])) {
|
|
napi_throw_error(env, nullptr, "Worker 1st param must be string with on");
|
|
return nullptr;
|
|
}
|
|
|
|
if (argc > 1 && !Helper::NapiHelper::IsCallable(env, args[1])) {
|
|
napi_throw_error(env, nullptr, "Worker 2st param must be callable with on");
|
|
return nullptr;
|
|
}
|
|
|
|
if (worker == nullptr) {
|
|
HILOG_ERROR("worker:: when post message to host occur worker is nullptr");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!worker->IsRunning()) {
|
|
// if worker is not running, don't send any message to host thread
|
|
HILOG_INFO("worker:: when post message to host occur worker is not in running.");
|
|
return nullptr;
|
|
}
|
|
|
|
napi_ref callback = nullptr;
|
|
if (argc > 1 && Helper::NapiHelper::IsCallable(env, args[1])) {
|
|
napi_create_reference(env, args[1], 1, &callback);
|
|
}
|
|
|
|
char* typeStr = Helper::NapiHelper::GetString(env, args[0]);
|
|
if (typeStr == nullptr) {
|
|
napi_throw_error(env, nullptr, "worker listener type create error, please check.");
|
|
return nullptr;
|
|
}
|
|
worker->ParentPortRemoveListenerInner(env, typeStr, callback);
|
|
Helper::CloseHelp::DeletePointer(typeStr, true);
|
|
Helper::NapiHelper::DeleteReference(env, callback);
|
|
return Helper::NapiHelper::GetUndefinedValue(env);
|
|
}
|
|
|
|
void Worker::ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener)
|
|
{
|
|
std::string typestr(type);
|
|
auto iter = parentPortEventListeners_.find(typestr);
|
|
if (iter == parentPortEventListeners_.end()) {
|
|
std::list<WorkerListener*> listeners;
|
|
listeners.emplace_back(const_cast<WorkerListener*>(listener));
|
|
parentPortEventListeners_[typestr] = listeners;
|
|
} else {
|
|
std::list<WorkerListener*>& listenerList = iter->second;
|
|
std::list<WorkerListener*>::iterator it = std::find_if(
|
|
listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_));
|
|
if (it != listenerList.end()) {
|
|
return;
|
|
}
|
|
listenerList.emplace_back(const_cast<WorkerListener*>(listener));
|
|
}
|
|
}
|
|
|
|
void Worker::ParentPortRemoveAllListenerInner()
|
|
{
|
|
for (auto iter = parentPortEventListeners_.begin(); iter != parentPortEventListeners_.end(); iter++) {
|
|
std::list<WorkerListener*>& listeners = iter->second;
|
|
for (auto item = listeners.begin(); item != listeners.end(); item++) {
|
|
WorkerListener* listener = *item;
|
|
Helper::CloseHelp::DeletePointer(listener, false);
|
|
}
|
|
}
|
|
parentPortEventListeners_.clear();
|
|
}
|
|
|
|
void Worker::ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback)
|
|
{
|
|
std::string typestr(type);
|
|
auto iter = parentPortEventListeners_.find(typestr);
|
|
if (iter == parentPortEventListeners_.end()) {
|
|
return;
|
|
}
|
|
std::list<WorkerListener*>& listenerList = iter->second;
|
|
if (callback != nullptr) {
|
|
std::list<WorkerListener*>::iterator it =
|
|
std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback));
|
|
if (it != listenerList.end()) {
|
|
Helper::CloseHelp::DeletePointer(*it, false);
|
|
listenerList.erase(it);
|
|
}
|
|
} else {
|
|
for (auto it = listenerList.begin(); it != listenerList.end(); it++) {
|
|
Helper::CloseHelp::DeletePointer(*it, false);
|
|
}
|
|
parentPortEventListeners_.erase(typestr);
|
|
}
|
|
}
|
|
|
|
void Worker::ParentPortHandleEventListeners(napi_env env, napi_value recv,
|
|
size_t argc, const napi_value* argv, const char* type)
|
|
{
|
|
std::string listener(type);
|
|
auto iter = parentPortEventListeners_.find(listener);
|
|
if (iter == parentPortEventListeners_.end()) {
|
|
HILOG_INFO("worker:: there is no listener for type %{public}s", type);
|
|
return;
|
|
}
|
|
|
|
std::list<WorkerListener*>& listeners = iter->second;
|
|
std::list<WorkerListener*>::iterator it = listeners.begin();
|
|
while (it != listeners.end()) {
|
|
WorkerListener* data = *it++;
|
|
napi_value callbackObj = Helper::NapiHelper::GetReferenceValue(env, data->callback_);
|
|
napi_value callbackResult = nullptr;
|
|
napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult);
|
|
if (!data->NextIsAvailable()) {
|
|
listeners.remove(data);
|
|
Helper::CloseHelp::DeletePointer(data, false);
|
|
}
|
|
}
|
|
}
|
|
} // namespace CompilerRuntime::WorkerModule
|