/// Drive `{{ service_pascal }}::{{ ep_pascal }}` from Java/Kotlin.
///
/// Parameters:
/// owner_handle: jlong returned by the service constructor entry point
/// ep params: as defined in the service entrypoint signature
#[no_mangle]
pub extern "system" fn {{ symbol }}(
_env: EnvUnowned,
_class: JClass,
owner_handle: jlong{{ params_decl }}
) -> jlong {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
// Validate owner handle
if owner_handle == 0 {
return 0; // Error: null pointer
}
// SAFETY: owner_handle was allocated by the constructor and is valid
// until freed. The caller is responsible for not using after free.
unsafe {
let owner_opaque = owner_handle as *mut {{ opaque_name }};
let owner_ref = &mut (*owner_opaque).inner;
// ~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 ENTRYPOINT_RUNTIME_STACK_SIZE_BYTES: usize = 16 * 1024 * 1024;
let rt = match tokio::runtime::Builder::new_multi_thread()
.enable_all()
.thread_stack_size(ENTRYPOINT_RUNTIME_STACK_SIZE_BYTES)
.build()
{
Ok(runtime) => runtime,
Err(_) => return 0, // Error: failed to create tokio runtime
};
let _result = rt.block_on(owner_ref.{{ ep_method }}({{ call_args }}));
// Finalize returns the transformed result; caller decides what to do with it
owner_handle
}
})).unwrap_or(0)
}