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

HarmonyOS
2024-12-19 16:14:26
980浏览
收藏 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)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
分享
微博
QQ
微信
回复
2024-12-19 18:54:16
相关问题
HarmonyOS napi_env如何线程使用
743浏览 • 1回复 待解决
为什么禁止缓存napi_env
1872浏览 • 1回复 待解决
NAPI执行上层回调时,如何获取env
3150浏览 • 1回复 待解决
如何NAPI执行上层回调时获取env
808浏览 • 1回复 待解决
IDE构建项目失败,该如何解决
1576浏览 • 1回复 待解决
HarmonyOS多线程使用?
783浏览 • 0回复 待解决
ArkTs多线程方案如何保证线程安全
3564浏览 • 2回复 待解决
如何使用taskpool实现多线程
2019浏览 • 1回复 待解决
HarmonyOS 多线程写法限制
840浏览 • 1回复 待解决