HarmonyOS napi_env env多线程访问失败,如何解决

HarmonyOS
3天前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
shlp

对于从napi接口层传入的env,需要在两个线程中同时访问该env环境— 不能这样用,这样会引起crash的。

请参考使用Node-API接口进行线程安全开发:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-thread-safety-0000001774280466

请参考根据上面链接,优化后提供的DEMO:index.d.ts文件:

export const jsRegister: (func: (data:number) => number) => void;
export const serviceNotifyFun: (data:number, type:number) => void;
//Index.ets文件:
import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Button("注册")
          .onClick(() => {
            testNapi.jsRegister((value: number) => {
              hilog.info(0x0000, 'testTag', 'js callback value:%{public}d', value);
              return (value + 11);
            })
          }).margin(10)

        Button("业务通知")
          .onClick(() => {
            for (let i:number = 0; i < 10; i++) {
              testNapi.serviceNotifyFun(i + 22, i + 55);
            }
          }).margin(10)
      }
      .width('100%')
    }
    .height('100%')
  }
}
//napi_init.cpp文件:
#include "napi/native_api.h"
#define LOG_TAG "testTag"
#include "hilog/log.h"
#include <pthread.h>
pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_ = PTHREAD_COND_INITIALIZER;
// 指向napi_value js_cb
napi_ref cbObj = nullptr;
// 线程安全函数
napi_threadsafe_function tsfn;
static int cValue = 999;

typedef struct TestData {
  int data;
  int type;
} TestData;

struct lnode {
  TestData data;
  struct lnode *next;
};

struct linkQueue {
  lnode *head;
  lnode *tail;
  int count;
};

struct linkQueue g_Queue;

static void InitQueue()
{
  g_Queue.head = nullptr;
  g_Queue.tail = nullptr;
  g_Queue.count = 0;
}
static bool Push(TestData &data)
{
  lnode *node = new lnode();
  if (node == nullptr) {
    return false;
  }
  node->next = nullptr;
  node->data = data;

  if (g_Queue.count == 0) {
    g_Queue.head = node;
    g_Queue.tail = node;
  } else {
    g_Queue.tail->next = node;
    g_Queue.tail = node;
  }
  g_Queue.count++;
  return true;
}

static TestData *Pop()
{
  if (g_Queue.count == 0) {
    return nullptr;
  }
  lnode *node = g_Queue.head;
  g_Queue.count--;
  g_Queue.head = g_Queue.head->next;
  TestData *data = new TestData();
  if (data == nullptr) {
    delete node;
    return nullptr;
  }
  *data = node->data;
  delete node;
  return data;
}

bool IsEmpty()
{
  return g_Queue.count == 0;
}
static napi_value ServiceNotifyFun(napi_env env, napi_callback_info info) {
  if (cbObj == nullptr) {
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "cbObj == nullptr");
    return nullptr;
  }
  size_t argc = 2;
  napi_value args[2] = {nullptr};

  napi_value ets_fun;
  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

  TestData testData;
  napi_get_value_int32(env, args[0], &testData.data);
  napi_get_value_int32(env, args[1], &testData.type);

  OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "ServiceNotifyFun:%{public}d,type:%{public}d", testData.data, testData.type);

  pthread_mutex_lock(&mutex_);
  Push(testData);
  pthread_mutex_unlock(&mutex_);
  pthread_cond_signal(&cond_);

  return nullptr;
}

// 回调
static void CallJs(napi_env env, napi_value js_cb, void *context, void *data) {
  TestData *testDatal = (TestData *)data;

  OH_LOG_INFO(LOG_APP, "CallJs, data=%{public}d, type:%{public}d", testDatal->data, testDatal->type);
  delete testDatal;

  OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, "testTag", "enter CallJs");

  // 获取引用值
  napi_get_reference_value(env, cbObj, &js_cb);

  // 创建一个ArkTS number作为ArkTS function的入参。
  napi_value argv;
  napi_create_int32(env, cValue, &argv);

  napi_value result = nullptr;
  napi_call_function(env, nullptr, js_cb, 1, &argv, &result);

  napi_get_value_int32(env, result, &cValue);

  OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, "testTag", "end CallJs, result:%{public}d", cValue);
}
static void *ServiceHandle(void *args) {
  OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "start thread_call");

  pthread_detach(pthread_self());

  TestData *testData = nullptr;

  while (true) {
    pthread_mutex_lock(&mutex_);
    pthread_cond_wait(&cond_, &mutex_);
    while (!IsEmpty()) {
      testData = Pop();
      if (testData == nullptr) {
        break;
      }
      OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "service Handle");
      napi_acquire_threadsafe_function(tsfn);
      napi_call_threadsafe_function(tsfn, testData, napi_tsfn_blocking);
      testData = nullptr;
    }
    pthread_mutex_unlock(&mutex_);
  }
  OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, "end thread_call");

  return nullptr;
}

static napi_value JsRegister(napi_env env, napi_callback_info info) {
  OH_LOG_INFO(LOG_APP, "start JsRegister");

  // 从ArkTS侧获取的参数的数量
  size_t argc = 1;
  napi_value js_cb, work_name;

  // 获取ArkTS参数
  napi_get_cb_info(env, info, &argc, &js_cb, nullptr, nullptr);

  // 指向napi_value js_cb 的 napi_ref cbObj
  napi_create_reference(env, js_cb, 1, &cbObj);

  // 通过UTF8编码的C字符串数据创建work_name
  napi_create_string_utf8(env, "Work Item", NAPI_AUTO_LENGTH, &work_name);

  // 创建线程安全函数
  napi_create_threadsafe_function(env, js_cb, NULL, work_name, 0, 1, NULL, NULL, NULL, CallJs, &tsfn);

  OH_LOG_INFO(LOG_APP, "end JsRegister");

  return nullptr;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
  napi_property_descriptor desc[] = {
  { "jsRegister", nullptr, JsRegister, nullptr, nullptr, nullptr, napi_default, nullptr },
{ "serviceNotifyFun", nullptr, ServiceNotifyFun, nullptr, nullptr, nullptr, napi_default, nullptr }
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);

InitQueue();
pthread_t tid;
pthread_create(&tid, NULL, &ServiceHandle, NULL);

return exports;
}
EXTERN_C_END

static napi_module demoModule = {
  .nm_version = 1,
  .nm_flags = 0,
  .nm_filename = nullptr,
  .nm_register_func = Init,
  .nm_modname = "entry",
  .nm_priv = ((void*)0),
  .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
  napi_module_register(&demoModule);
}
CMakeLists.txt文件:
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(napi_call_threadsafe_function)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()

include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)

add_library(entry SHARED napi_init.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so)
分享
微博
QQ
微信
回复
3天前
相关问题
为什么禁止缓存napi_env
1068浏览 • 1回复 待解决
NAPI执行上层回调时,如何获取env
2284浏览 • 1回复 待解决
如何NAPI执行上层回调时获取env
350浏览 • 1回复 待解决
IDE构建项目失败,该如何解决
716浏览 • 1回复 待解决
ArkTs多线程方案如何保证线程安全
2581浏览 • 2回复 待解决
HarmonyOS 多线程写法限制
149浏览 • 1回复 待解决
如何使用taskpool实现多线程
1154浏览 • 1回复 待解决
HarmonyOS 多线程并发相关问题
247浏览 • 1回复 待解决
多线程中EGL如何共享Context
432浏览 • 1回复 待解决
HarmonyOS toast问题如何解决
820浏览 • 1回复 待解决
HarmonyOS 数据库多线程操作
184浏览 • 1回复 待解决