react-native 从C++线程异步调用JS回调函数,

j9per5c4  于 4个月前  发布在  React
关注(0)|答案(7)|浏览(108)

我遇到了一个问题,当我在C线程上异步调用C代码中的JavaScript回调时,我的React Native应用崩溃了。然而,当我在同一线程上从foo函数调用相同的回调时,它按预期工作,没有崩溃。

描述

我在遇到一个问题,当我的React Native应用在C线程上异步调用C代码中的JavaScript回调时崩溃。然而,当我在同一线程上从foo函数调用相同的回调时,它按预期工作,没有崩溃。

重现步骤

React Native版本

0.74.1

受影响的平台

运行时 - Android,构建 - Windows

领域

TurboModule - 新的本地模块系统,JSI - JavaScript接口,Bridgeless - 新的初始化流程

npx react-native info的输出

System:
  OS: Windows 10 10.0.19045
  CPU: (4) x64 Intel(R) Core(TM) i5-6500T CPU @ 2.50GHz
  Memory: 6.54 GB / 15.88 GB
Binaries:
  Node:
    version: 20.13.0
    path: E:\Program Files\nodejs\node.EXE
  Yarn:
    version: 3.6.1
    path: ~\AppData\Roaming\npm\yarn.CMD
  npm:
    version: 10.7.0
    path: E:\Program Files\nodejs\npm.CMD
  Watchman: Not Found
SDKs:
  Android SDK:
    API Levels:
      - "29"
      - "30"
      - "31"
      - "34"
    Build Tools:
      - 30.0.2
      - 30.0.3
      - 33.0.1
      - 34.0.0
    System Images:
      - android-30 | Intel x86_64 Atom
      - android-30 | Google Play Intel x86 Atom
      - android-30 | Google APIs ATD Intel x86_64 Atom
      - android-34 | Google APIs Intel x86_64 Atom
    Android NDK: Not Found
  Windows SDK: Not Found
IDEs:
  Android Studio: Not Found
  Visual Studio: Not Found
Languages:
  Java: 17.0.11
  Ruby: Not Found
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.1
    wanted: 0.74.1
  react-native-windows: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: false
  newArchEnabled: true
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

堆栈跟踪或日志

06-06 09:22:40.260 21837 21859 W libEGL  : EGLNativeWindowType 0xb40000786029bbc0 disconnect failed                     06-06 09:22:40.313 21837 22216 I MyApp   : Log message to Android                                                       06-06 09:22:40.367 21837 22216 I ReactNativeJS: 'error:', undefined, 'result:', 'hello world'                           06-06 09:22:40.369 21837 22216 E libc++abi: terminating due to uncaught exception of type facebook::jsi::JSError: Can't find variable: setHello                                                                                                 06-06 09:22:40.369 21837 22216 E libc++abi:                                                                             06-06 09:22:40.369 21837 22216 E libc++abi: callback                                                                    06-06 09:22:40.369 21837 22216 E libc++abi: onFastRefresh@http://localhost:8081/index.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.bikegadgetmainappexample&modulesOnly=false&runModule=true:44263:35                        06-06 09:22:40.369 21837 22216 E libc++abi: performReactRefresh@http://localhost:8081/index.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.bikegadgetmainappexample&modulesOnly=false&runModule=true:62080:34                  06-06 09:22:40.369 21837 22216 E libc++abi: http://localhost:8081/index.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.bikegadgetmainappexample&modulesOnly=false&runModule=true:435:40                                        --------- beginning of crash                                                                                            06-06 09:22:40.370 21837 22216 F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 22216 (mqt_v_js), pid 21837 (tmainappexample)                                                                                                    06-06 09:22:40.472 22219 22219 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstone           06-06 09:22:40.473   249   249 I tombstoned: received crash request for pid 22216                                       06-06 09:22:40.481 22219 22219 I crash_dump64: performing dump of process 21837 (target tid = 22216)                    06-06 09:22:40.497 22219 22219 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***              06-06 09:22:40.498 22219 22219 F DEBUG   : Build fingerprint: 'Firefly/rk356x/rk356x:11/RD2A.211001.002/lwy07101153:userdebug/release-keys'                                                                                                     06-06 09:22:40.498 22219 22219 F DEBUG   : Revision: '0'                                                                06-06 09:22:40.498 22219 22219 F DEBUG   : ABI: 'arm64'                                                                 06-06 09:22:40.499 22219 22219 F DEBUG   : Timestamp: 2024-06-06 09:22:40+0000                                          06-06 09:22:40.499 22219 22219 F DEBUG   : pid: 21837, tid: 22216, name: mqt_v_js  >>> com.bikegadgetmainappexample <<< 06-06 09:22:40.499 22219 22219 F DEBUG   : uid: 10120                                                                   06-06 09:22:40.499 22219 22219 F DEBUG   : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------                  06-06 09:22:40.499 22219 22219 F DEBUG   : Abort message: 'terminating due to uncaught exception of type facebook::jsi::JSError: Can't find variable: setHello

可复现问题

JSI-App

截图和视频

App.tsx

import * as React from 'react';
import { StyleSheet, View, Text } from 'react-native';
import { multiply, install} from 'react-native-bikegadget-mainapp';

const callback = (error, result) => {
  console.log('error:', error, 'result:', result);
  setHello("message from js");
}

export default function App() {
  const [result, setResult] = React.useState(multiply(4, 6));
  const [hello, setHello] = React.useState('');

  React.useEffect(() => {
    install();
    setHello(helloWorld());
    foo(callback);
    
    // callback("err123", "hello")
  }, []);

  return (
    <View style={styles.container}>
      <Text>Result: {result}</Text>
      <Text>Hello: {hello}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  box: {
    width: 60,
    height: 60,
    marginVertical: 20,
  },
});

C++模块

#include "react-native-bikegadget-mainapp.h"
#include <jsi/jsi.h>
#include <jsi/jsilib.h>
#include <thread>
#include <android/log.h>
#include <ReactCommon/CallInvoker.h>

using namespace facebook::jsi;
using namespace std;

namespace bikegadgetmainapp
{
	// Function to register helloWorld in JSI
	void registerHelloWorld(Runtime &jsiRuntime)
	{
		auto helloWorld = Function::createFromHostFunction(
			jsiRuntime,
			PropNameID::forAscii(jsiRuntime, "helloWorld"),
			0,
			[](Runtime &runtime,
			   const Value &thisValue,
			   const Value *arguments,
			   size_t count) -> Value
			{
				string helloworld = "helloworld Ash 123";
				return Value(runtime, String::createFromUtf8(runtime, helloworld));
			});

		jsiRuntime.global().setProperty(jsiRuntime, "helloWorld", std::move(helloWorld));
	}

	void registerFoo(Runtime &jsiRuntime)
	{
		auto foo = Function::createFromHostFunction(
			jsiRuntime,
			PropNameID::forAscii(jsiRuntime, "foo"),
			1,
			[](Runtime &runtime, const Value &thisValue, const Value *arguments, size_t count) -> Value
			{
				if (count < 1 || !arguments[0].isObject() || !arguments[0].getObject(runtime).isFunction(runtime))
				{
					throw JSError(runtime, "Expected a function as the first argument");
				}

				// Create a shared reference to the user callback
				auto userCallbackRef = std::make_shared<Object>(arguments[0].getObject(runtime));

				// Lambda to be run in the new thread
				auto f = [&runtime, userCallbackRef]()
				{
					auto val = std::make_shared<std::string>("hello world");
					auto error = std::make_shared<Value>(Value::undefined());

					// Periodically log messages to Android log and sleep for 5 seconds
					while (true)
					{
						__android_log_print(ANDROID_LOG_INFO, "MyApp", "Log message to Android");
                        userCallbackRef->asFunction(runtime).call(runtime, *error, *val);
						std::this_thread::sleep_for(std::chrono::seconds(5));
					}
				};

				// // Launch the lambda in a new thread
				std::thread thread_object(f);
				thread_object.detach();

				return Value::undefined();
			});

		jsiRuntime.global().setProperty(jsiRuntime, "foo", std::move(foo));
	}
	// Function to install the JSI bindings
	void install(Runtime &jsiRuntime)
	{
		// Register the helloWorld function
		registerHelloWorld(jsiRuntime);
		registerFoo(jsiRuntime);
	}

	double multiply(double a, double b)
	{
		return a * b;
	}
}
abithluo

abithluo1#

⚠️React Native的新版本可用!
i️您正在使用受支持的次要版本,但似乎有一个更新的补丁可用 - 0.74.2。请升级到您次要版本的最高补丁或最新版本,并验证问题是否仍然存在(或者,创建一个新项目并在其中重现问题)。如果无法重现,请告知我们,以便我们关闭此问题。这有助于确保我们关注仍在最近版本中存在的问题。
ki0zmccv

ki0zmccv2#

请使用 Reproducer Template 创建一个复现案例。

qvk1mo1f

qvk1mo1f3#

@cortinico 我已经更新了问题,并添加了所需的ReproducerApp。

k7fdbhmy

k7fdbhmy4#

jsi.h 中,它指定了 JSRuntime 不能同时被访问。这个崩溃是预期的。
表示一个 JS 运行时。可移动,但不可复制。请注意,
这个对象可能不是线程相关的,但不能同时从多个线程安全地使用。应用程序负责确保其安全使用。这可能意味着在一个线程中使用 Runtime,使用互斥锁,在串行队列上完成所有工作等。此限制适用于此类的方法,以及 API 中任何接受 Runtime& 作为参数的方法。析构函数(除了 ~Scope),操作符或其他不接受 Runtime& 作为参数的方法可以从任何线程安全地调用,但仍然禁止在多个线程中对任何类的单个示例进行写操作。此外,为了使关闭安全,必须在 Runtime 销毁之前销毁与 Runtime 相关联的对象,或者从托管的 HostObject 或 HostFunction 的析构函数中进行。非正式地说,这意味着主要的不安全行为来源是在非 Runtime-管理的 object 中持有 jsi 对象,并且在 Runtime 关闭之前不清理它。如果您的生命周期使得避免这一点很困难,您可能需要使用自己的锁。

class JSI_EXPORT Runtime {}
ryevplcw

ryevplcw5#

假设在C中只有一个线程访问JS运行时,那么我们如何阻止JavaScript在C使用它时使用运行时?

6ovsh4lw

6ovsh4lw6#

假设在C中只有一个线程访问JS运行时,那么我们如何阻止JavaScript在C使用它时使用运行时?

jsi.h中,它提出了以下解决方案。这可能意味着只在一个线程中使用运行时,使用互斥锁,在串行队列上执行所有工作等。这取决于你想要实现什么。在你发布的代码中,我假设你想在另一个线程中分派任务,并通过回调函数在JS线程中返回结果。你可以尝试像串行队列或ReactContext.runOnJSQueueThread这样的方法,它们都需要一些额外的开发,并且超出了这个问题的范围。我建议你调查一下更多的开源C++ RN模块,学习它们的设计/实现。

blmhpbnm

blmhpbnm7#

你好,@AbeereSpark。为了确保在异步操作期间从cpp文件安全地调用JS函数,你必须使用一个jsCallInvoker。你的应用程序崩溃了,因为很可能你是想访问已经被分配给其他需求的内存。

首先,从桥接上下文中获取jsCallInvoker:
对于iOS

auto& runtime = *jsiRuntime;
auto callInvocker = bridge.jsCallInvoker;
install(runtime, callInvocker); // install - cpp function

对于Android

this.reactApplicationContext.javaScriptContextHolder?.let { contextHolder ->
    this.reactApplicationContext.catalystInstance.jsCallInvokerHolder?.let { callInvokerHolder: CallInvokerHolder ->
        this.nativeInstall(contextHolder.get(), callInvokerHolder)
        return true
    }
}

private external fun nativeInstall(jsi: Long, callInvoker: CallInvokerHolder)

在cpp中

void install(Runtime &jsiRuntime, std::shared_ptr<react::CallInvoker> callInvoker)
	{
		// Register the helloWorld function
		registerHelloWorld(jsiRuntime);
		registerFoo(jsiRuntime);
	}

然后你就可以使用callInvoker,像这样:

while (true)
	{
		__android_log_print(ANDROID_LOG_INFO, "MyApp", "Log message to Android");
		callInvoker->invokeAsync([&runtime, error, val, userCallbackRef]{
				userCallbackRef->asFunction(runtime).call(runtime, *error, *val);
		});
		std::this_thread::sleep_for(std::chrono::seconds(5));
	}

我还没有检查这段代码,也许你需要处理指针,但大致思路是正确的

相关问题