{%- for param in clone_params %}
let {{ param.name }} = {{ param.name }}.clone();
{%- endfor %}
let reply_id = TRAIT_REPLY_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let (tx, rx) = tokio::sync::oneshot::channel::<Result<String, String>>();
TRAIT_REPLY_CHANNELS.lock().unwrap().insert(reply_id, tx);
arm_trait_call_timeout(reply_id);
let pid = self.inner;
let args_json = {
let {% if params %}mut {% endif %}args = serde_json::Map::new();
{%- for param in params %}
args.insert("{{ param.name }}".to_string(), {{ param.json_expr }});
{%- endfor %}
serde_json::Value::Object(args).to_string()
};
let method = "{{ method_name }}";
// Check if we're already in a tokio runtime context. If not, use block_on.
let result = if tokio::runtime::Handle::try_current().is_err() {
// ~keep 16 MiB: tokio's ~2 MB default worker stack can overflow on a deep extraction
// future (a nested archive member, a multi-stage OCR pipeline), and a stack overflow
// aborts the process with SIGBUS instead of raising a catchable panic.
const TRAIT_BRIDGE_RUNTIME_STACK_SIZE_BYTES: usize = 16 * 1024 * 1024;
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(TRAIT_BRIDGE_RUNTIME_STACK_SIZE_BYTES)
.build()
.unwrap();
rt.block_on(async {
tokio::task::spawn_blocking(move || {
let mut env = rustler::OwnedEnv::new();
let _ = env.send_and_clear(&pid, |env| {
(rustler::types::atom::Atom::from_str(env, "trait_call").unwrap(),
method, args_json.as_str(), reply_id).encode(env)
});
}).await.ok();
rx.await
})
} else {
drop(tokio::task::spawn_blocking(move || {
let mut env = rustler::OwnedEnv::new();
let _ = env.send_and_clear(&pid, |env| {
(rustler::types::atom::Atom::from_str(env, "trait_call").unwrap(),
method, args_json.as_str(), reply_id).encode(env)
});
}));
rx.blocking_recv()
};
match result {
{%- if has_error %}
Ok(Ok(json)) => serde_json::from_str(&json).map_err(|_e| {{ error_deser }}),
Ok(Err(msg)) => Err({{ error_msg }}),
Err(_) => Err({{ error_closed }}),
{%- else %}
Ok(Ok(json)) => serde_json::from_str(&json).unwrap_or_default(),
_ => Default::default()
{%- endif %}
}