HarmonyOS native 中执行长耗时任务收到中断信号,导致函数异常终止

操作步骤:

1、按照这个文档开发native模块:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/use-napi-asynchronous-task-V5

2、ExecuteCB中执行我们的业务逻辑 dowork()

  1. ArtTS中调用对应的接口

  2. ExecuteCB执行到dowork,当dowork执行到1秒时,执行被中断并伴随log:

05-29 18:34:19.458   3399-3417    C01719/ffrt                    com.examp...lication  W     [nodict]349:RecordSymbolAndBacktrace:114 Function occupies worker for more than 1s.

从log中看,worker是基于ffrt实现的,看了开源的

https://gitee.com/openharmony/resourceschedule_ffrt

是ffrt的长耗时任务监测触发的抓栈信号。

https://gitee.com/openharmony/resourceschedule_ffrt/blob/master/docs/user_guide.md

中,提到的sleep被中断唤醒的处理方式不适用于我们的场景:在dowork()函数中,包含与服务器交互逻辑,当收到中断时,与服务器连接状态都被重置,无法从断点恢复。

我们的需求:

能提供一个运行长耗时native任务的机制,且在执行过程中不被中断。

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

建议将这部分逻辑修改如下:

1.将与服务器交互逻辑在一个专门线程中处理。

2.业务逻辑处理完毕后,在通过线程安全函数将响应的信息返回给ArkTS层。

如下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
微信
回复
9天前
相关问题
HarmonyOS 主线程耗时任务,app闪退
142浏览 • 1回复 待解决
HarmonyOS时任务
132浏览 • 1回复 待解决
HarmonyOS nativesvg解码异常
516浏览 • 1回复 待解决
"NAPI通信耗时长"导致丢帧分析
859浏览 • 1回复 待解决
后台长时任务启动失败
2322浏览 • 1回复 待解决
如何申请多个长时任务
2097浏览 • 1回复 待解决