Skip to content

Commit

Permalink
Fix cross threading issue when calling callStatic Method
Browse files Browse the repository at this point in the history
  • Loading branch information
qiuguohua committed Feb 11, 2025
1 parent 4a88af7 commit b741877
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 101 deletions.
74 changes: 8 additions & 66 deletions native/cocos/bindings/manual/JavaScriptArkTsBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,79 +107,21 @@ bool JavaScriptArkTsBridge::CallInfo::execute(se::Value& rval) {
module_name = pos != std::string::npos ? pathStr.substr(0, pos).c_str() : "entry";
}

auto env = cc::NapiHelper::getWorkerEnv();

napi_status status;
napi_value result;
char* module_info = __getModuleInfo(module_name);
status = napi_load_module_with_info(env, _clsPath, module_info, &result);
free(module_info);
module_info = nullptr;
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_load_module_with_info fail, status=%{public}d", status);
return false;
}

napi_value func;
status = napi_get_named_property(env, result, method, &func);
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_get_named_property fail, status=%{public}d", status);
return false;
}

if (_isSyn) {
napi_value jsArg = Napi::String::New(env, _paramStr);
napi_value return_val;
status = napi_call_function(env, result, func, 1, &jsArg, &return_val);
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_call_function fail, status=%{public}d", status);
return false;
}

napi_valuetype valueType;
status = napi_typeof(env, return_val, &valueType);
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_typeof fail, status=%{public}d", status);
return false;
}

switch (valueType) {
case napi_string: {
size_t str_size;
napi_get_value_string_utf8(env, return_val, nullptr, 0, &str_size);
char* buf = new char[str_size + 1];
napi_get_value_string_utf8(env, return_val, buf, str_size + 1, &str_size);
rval = se::Value(std::string(buf));
delete[] buf;
break;
}
case napi_number: {
double num_value;
napi_get_value_double(env, return_val, &num_value);
CC_LOG_INFO("Returned number: %f", num_value);
rval = se::Value(num_value);
break;
}
case napi_boolean: {
bool bool_value;
napi_get_value_bool(env, return_val, &bool_value);
rval = se::Value(bool_value);
break;
}
default:
CC_LOG_WARNING("Unhandled return value type");
break;
}
return true;
}

std::promise<cc::CallbackParamType> promise;
std::function<void(cc::CallbackParamType)> cb = [&promise](cc::CallbackParamType message) {
promise.set_value(message);
};
cc::AsyncCallParam* callParam = new cc::AsyncCallParam{cb, _paramStr, func};
cc::JSFunction::getFunction("executeNativeMethod").invokeAsync(callParam);
cc::AsyncCallParam *callParam = new cc::AsyncCallParam{cb, _paramStr, module_info, _clsPath, method};
if (_isSyn) {
cc::JSFunction::getFunction("executeMethodSync").invoke(callParam, _isSyn);
} else {
cc::JSFunction::getFunction("executeMethodAsync").invoke(callParam, _isSyn);
}
cc::CallbackParamType methodResult = promise.get_future().get();
free(module_info);
delete callParam;
rval = se::Value(convertToSeValue(methodResult));
return true;
}
Expand Down
147 changes: 116 additions & 31 deletions native/cocos/platform/openharmony/napi/NapiHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,65 +26,66 @@
#pragma once

#define NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS 1
#define NAPI_DISABLE_CPP_EXCEPTIONS 1
#define NODE_ADDON_API_DISABLE_DEPRECATED 1
#include "napi.h"
#define NAPI_DISABLE_CPP_EXCEPTIONS 1
#define NODE_ADDON_API_DISABLE_DEPRECATED 1
#include "NapiValueConverter.h"
#include "base/Log.h"
#include "napi.h"

namespace cc {
using CallbackParamType = std::variant<std::string, double, bool>;

struct AsyncCallParam {
std::function<void(CallbackParamType)> cb;
std::string paramStr;
napi_value callFunc;
char *module_info;
const char *clsPath;
const char *method;
napi_ref executeFuncRef;
};

class NapiHelper {
public:
static Napi::Env getWorkerEnv();
static Napi::Object init(Napi::Env env, Napi::Object exports);
static Napi::Value napiCallFunction(const char* functionName);
static Napi::Value napiCallFunction(const char *functionName,float duration);
static void postMessageToUIThread(const std::string& type, Napi::Value param);
static Napi::Value postSyncMessageToUIThread(const std::string& type, Napi::Value param);
static Napi::Value napiCallFunction(const char *functionName);
static Napi::Value napiCallFunction(const char *functionName, float duration);
static void postMessageToUIThread(const std::string &type, Napi::Value param);
static Napi::Value postSyncMessageToUIThread(const std::string &type, Napi::Value param);
};

class JSFunction {
public:
napi_ref funcRef;
napi_env env;
char* name = nullptr;
char *name = nullptr;

public:
static std::unordered_map<std::string, JSFunction> jsFunctionMap;

explicit JSFunction(char* name, napi_env env, napi_ref funcRef)
: name(name), env(env), funcRef(funcRef){}
explicit JSFunction(char *name, napi_env env, napi_ref funcRef)
: name(name), env(env), funcRef(funcRef) {}

explicit JSFunction(char* name, napi_env env)
: name(name), env(env){}
explicit JSFunction(char *name, napi_env env)
: name(name), env(env) {}

explicit JSFunction(char* name)
: name(name){}
explicit JSFunction(char *name)
: name(name) {}

static JSFunction getFunction(std::string functionName)
{
static JSFunction getFunction(std::string functionName) {
return jsFunctionMap.at(functionName);
}

static void addFunction(std::string name, JSFunction* jsFunction) {
static void addFunction(std::string name, JSFunction *jsFunction) {
jsFunctionMap.emplace(name, *jsFunction);
}

template<typename ReturnType, typename... Args>
template <typename ReturnType, typename... Args>
typename std::enable_if<!std::is_same<ReturnType, void>::value, ReturnType>::type
invoke(Args... args) {
napi_value global;
napi_status status = napi_get_global(env, &global);
//if (status != napi_ok) return;
// if (status != napi_ok) return;

napi_value func;
status = napi_get_reference_value(env, funcRef, &func);
Expand All @@ -100,7 +101,7 @@ class JSFunction {
return value;
}

template<typename ReturnType, typename... Args>
template <typename ReturnType, typename... Args>
typename std::enable_if<std::is_same<ReturnType, void>::value, void>::type
invoke(Args... args) {
napi_value global;
Expand All @@ -115,7 +116,7 @@ class JSFunction {
status = napi_call_function(env, global, func, sizeof...(Args), jsArgs, &return_val);
}

void invokeAsync(AsyncCallParam *callParam) {
void invoke(AsyncCallParam *callParam, bool isSync) {
callParam->executeFuncRef = funcRef;

napi_status status;
Expand All @@ -134,11 +135,14 @@ class JSFunction {
}

napi_threadsafe_function save_func;
status = napi_create_threadsafe_function(env, func, nullptr, workName, 0, 1, nullptr,
[](napi_env env, void *raw, void *hint) {}, callParam, CallJS, &save_func);
if (status != napi_ok) {
CC_LOG_WARNING("invokeAsync napi_create_threadsafe_function fail,status=%{public}d", status);
return;
if (isSync) {
status = napi_create_threadsafe_function(
env, func, nullptr, workName, 0, 1, nullptr, [](napi_env env, void *raw, void *hint) {}, callParam,
CallJsSync, &save_func);
} else {
status = napi_create_threadsafe_function(
env, func, nullptr, workName, 0, 1, nullptr, [](napi_env env, void *raw, void *hint) {}, callParam,
CallJsAsync, &save_func);
}

status = napi_acquire_threadsafe_function(save_func);
Expand All @@ -154,8 +158,8 @@ class JSFunction {
}
}

static void CallJS(napi_env env, napi_value js_cb, void *context, void *data) {
AsyncCallParam *callParam = (AsyncCallParam*) (context);
static void CallJsAsync(napi_env env, napi_value js_cb, void *context, void *data) {
AsyncCallParam *callParam = (AsyncCallParam *)(context);
if (callParam == nullptr) {
CC_LOG_WARNING("CallJS AsyncCallParam callParam is null");
return;
Expand Down Expand Up @@ -202,6 +206,7 @@ class JSFunction {
callbackValue = resultBol;
} else {
callbackValue = "unknown";
CC_LOG_WARNING("callbackValue returns incorrect value type");
}
callbackParam->cb(callbackValue);
return return_val;
Expand All @@ -214,7 +219,21 @@ class JSFunction {
return;
}

napi_value jsArgs[3] = {callParam->callFunc, NapiValueConverter::ToNapiValue(env, callParam->paramStr), callbackFunc};
napi_value result;
status = napi_load_module_with_info(env, callParam->clsPath, callParam->module_info, &result);
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_load_module_with_info fail, status=%{public}d", status);
return;
}

napi_value callFunc;
status = napi_get_named_property(env, result, callParam->method, &callFunc);
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_get_named_property fail, status=%{public}d", status);
return;
}

napi_value jsArgs[3] = {callFunc, NapiValueConverter::ToNapiValue(env, callParam->paramStr), callbackFunc};
napi_value return_val;
napi_value global;
status = napi_get_global(env, &global);
Expand All @@ -227,6 +246,72 @@ class JSFunction {
CC_LOG_WARNING("CallJS napi_call_function fail,status=%{public}d", status);
}
}

static void CallJsSync(napi_env env, napi_value js_cb, void *context, void *data) {
AsyncCallParam *callParam = (AsyncCallParam *)(context);
if (callParam == nullptr) {
CC_LOG_WARNING("CallJS AsyncCallParam callParam is null");
return;
}

napi_status status;
status = napi_get_reference_value(env, callParam->executeFuncRef, &js_cb);
if (status != napi_ok) {
CC_LOG_WARNING("CallJS napi_get_reference_value fail,status=%{public}d", status);
return;
}

napi_value result;
status = napi_load_module_with_info(env, callParam->clsPath, callParam->module_info, &result);
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_load_module_with_info fail, status=%{public}d", status);
return;
}

napi_value callFunc;
status = napi_get_named_property(env, result, callParam->method, &callFunc);
if (status != napi_ok) {
CC_LOG_WARNING("callNativeMethod napi_get_named_property fail, status=%{public}d", status);
return;
}

napi_value jsArgs[2] = {callFunc, NapiValueConverter::ToNapiValue(env, callParam->paramStr)};
napi_value return_val;
napi_value global;
status = napi_get_global(env, &global);
if (status != napi_ok) {
CC_LOG_WARNING("CallJS napi_get_global fail,status=%{public}d", status);
}

status = napi_call_function(env, global, js_cb, 2, jsArgs, &return_val);

napi_valuetype type;
napi_typeof(env, return_val, &type);

CallbackParamType callbackValue;

if (type == napi_string) {
std::string resultStr;
NapiValueConverter::ToCppValue(env, return_val, resultStr);
callbackValue = std::move(resultStr);
} else if (type == napi_number) {
double resultNum;
NapiValueConverter::ToCppValue(env, return_val, resultNum);
callbackValue = resultNum;
} else if (type == napi_boolean) {
bool resultBol;
NapiValueConverter::ToCppValue(env, return_val, resultBol);
callbackValue = resultBol;
} else {
callbackValue = "unknown";
CC_LOG_WARNING("callbackValue returns incorrect value type");
}
callParam->cb(callbackValue);

if (status != napi_ok) {
CC_LOG_WARNING("CallJS napi_call_function fail,status=%{public}d", status);
}
}
};

} // namespace cc
} // namespace cc
4 changes: 2 additions & 2 deletions scripts/native-pack-tool/source/platforms/harmonyos-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ export class HarmonyOSNextPackTool extends NativePackTool {
moduleJSON.module.abilities[0].orientation = 'auto_rotation';
}
else if (cfg.landscapeRight && !cfg.landscapeLeft) {
moduleJSON.module.abilities[0].orientation = 'landscape_inverted';
moduleJSON.module.abilities[0].orientation = 'landscape';
}
else if (!cfg.landscapeRight && cfg.landscapeLeft) {
moduleJSON.module.abilities[0].orientation = 'landscape';
moduleJSON.module.abilities[0].orientation = 'landscape_inverted';
}
else if (cfg.landscapeRight && cfg.landscapeLeft) {
moduleJSON.module.abilities[0].orientation = 'auto_rotation_landscape';
Expand Down
8 changes: 6 additions & 2 deletions templates/harmonyos-next/entry/src/main/ets/pages/index.ets
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ const nativePageLifecycle :context = nativerender.getContext(ContextType.JSPAGE_
const engineUtils :context = nativerender.getContext(ContextType.ENGINE_UTILS);


function executeNativeMethod(nativeFunc: Function, funcData: string, funCb: Function): void {
function executeMethodAsync(nativeFunc: Function, funcData: string, funCb: Function): void {
nativeFunc && nativeFunc(funcData, funCb);
}
engineUtils.registerFunction("executeNativeMethod", executeNativeMethod);
function executeMethodSync(nativeFunc: Function, funcData: string): string|boolean|number {
return nativeFunc && nativeFunc(funcData);
}
engineUtils.registerFunction("executeMethodAsync", executeMethodAsync);
engineUtils.registerFunction("executeMethodSync", executeMethodSync);

interface WorkerMessage {
type: string;
Expand Down

0 comments on commit b741877

Please sign in to comment.