This project makes it easier to integrate WAMR into ESP32, allowing you to focus on application logic without dealing with underlying pthread issues.
When running WAMR on ESP32, you might encounter crashes, especially errors related to POSIX threads (pthread). Research indicates that this happens because WAMR expects to run within a pthread context. However, the main program (app_main) of ESP32 is actually a FreeRTOS task, not a pthread. As a result, WAMR's internal pthread functions (e.g., pthread_self) fail because they cannot retrieve the current thread ID in a non-pthread context.
According to the official documentation POSIX Threads Support - ESP32:
pthread_self()
An assert will fail if this function is called from a FreeRTOS task which is not a pthread.
This explains why the original WAMR crashes.
The wamr-rust-pthreadcall library provides the call_pthread function, which resolves this issue by executing WAMR functions within the correct pthread context. This prevents crashes and ensures WAMR runs stably on ESP32.
Using this library is straightforward. First, add the dependency in your Cargo.toml file:
[dependencies]
wamr-rust-pthreadcall = { git="https://github.com/darkautism/wamr-rust-pthreadcall.git" }Then, in your Rust code, use call_pthread to replace the original call method. Here's an example:
use wamr_rust_pthreadcall::PThreadExtension;
use wamr_rust_sdk::function::Function;
use wamr_rust_sdk::instance::Instance;
use wamr_rust_sdk::value::WasmValue;
let params: Vec<WasmValue> = vec![];
let result = function
.call_pthread(&instance, ¶ms)
.expect("Failed to call WAMR function");
log::info!("Program exited with code: {:?}", result);If your need more object running into pthread, you can use another function:
let a: Result<Vec<WasmValue>, RuntimeError> =
wamr_rust_pthreadcall::call_pthread(4096, move || {
let runtime = match Runtime::builder()
.use_system_allocator()
.register_host_function(
"vTaskDelay",
esp_idf_svc::sys::vTaskDelay as *mut std::ffi::c_void,
)
.build()
{
Ok(runtime) => runtime,
Err(e) => {
log::error!("Failed to load WAMR runtime: {}", e);
return Err(e);
}
};
let module = match Module::from_file(&runtime, d.as_path()) {
Ok(module) => module,
Err(e) => {
log::error!("Failed to load WAMR module: {}", e);
return Err(e);
}
};
let instance = match Instance::new(&runtime, &module, config::GAMEMEMORY) {
Ok(instance) => instance,
Err(e) => {
log::error!("Failed to create WAMR instance: {}", e);
return Err(e);
}
};
let function = match Function::find_export_func(&instance, config::ENTRYPOINT) {
Ok(function) => function,
Err(e) => {
log::error!("Failed to find WAMR function: {}", e);
return Err(e);
}
};
let params: Vec<WasmValue> = vec![];
function.call(&instance, ¶ms)
})
.expect("Pthread create failed");This way, the library automatically handles the creation and management of pthreads, ensuring WebAssembly functions are executed in the correct context.