我遇到了一个问题,当我在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
可复现问题
截图和视频
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;
}
}
7条答案
按热度按时间abithluo1#
ki0zmccv2#
请使用 Reproducer Template 创建一个复现案例。
qvk1mo1f3#
@cortinico 我已经更新了问题,并添加了所需的ReproducerApp。
k7fdbhmy4#
在
jsi.h
中,它指定了 JSRuntime 不能同时被访问。这个崩溃是预期的。表示一个 JS 运行时。可移动,但不可复制。请注意,
这个对象可能不是线程相关的,但不能同时从多个线程安全地使用。应用程序负责确保其安全使用。这可能意味着在一个线程中使用 Runtime,使用互斥锁,在串行队列上完成所有工作等。此限制适用于此类的方法,以及 API 中任何接受 Runtime& 作为参数的方法。析构函数(除了 ~Scope),操作符或其他不接受 Runtime& 作为参数的方法可以从任何线程安全地调用,但仍然禁止在多个线程中对任何类的单个示例进行写操作。此外,为了使关闭安全,必须在 Runtime 销毁之前销毁与 Runtime 相关联的对象,或者从托管的 HostObject 或 HostFunction 的析构函数中进行。非正式地说,这意味着主要的不安全行为来源是在非 Runtime-管理的 object 中持有 jsi 对象,并且在 Runtime 关闭之前不清理它。如果您的生命周期使得避免这一点很困难,您可能需要使用自己的锁。
ryevplcw5#
假设在C中只有一个线程访问JS运行时,那么我们如何阻止JavaScript在C使用它时使用运行时?
6ovsh4lw6#
假设在C中只有一个线程访问JS运行时,那么我们如何阻止JavaScript在C使用它时使用运行时?
在
jsi.h
中,它提出了以下解决方案。这可能意味着只在一个线程中使用运行时,使用互斥锁,在串行队列上执行所有工作等。这取决于你想要实现什么。在你发布的代码中,我假设你想在另一个线程中分派任务,并通过回调函数在JS线程中返回结果。你可以尝试像串行队列或ReactContext.runOnJSQueueThread
这样的方法,它们都需要一些额外的开发,并且超出了这个问题的范围。我建议你调查一下更多的开源C++ RN模块,学习它们的设计/实现。blmhpbnm7#
你好,@AbeereSpark。为了确保在异步操作期间从cpp文件安全地调用JS函数,你必须使用一个jsCallInvoker。你的应用程序崩溃了,因为很可能你是想访问已经被分配给其他需求的内存。
首先,从桥接上下文中获取jsCallInvoker:
对于iOS
对于Android
在cpp中
然后你就可以使用callInvoker,像这样:
我还没有检查这段代码,也许你需要处理指针,但大致思路是正确的