/// Drive `{{ owner_path }}::{{ ep_method }}` from Ruby.
///
/// Each entry in `registrations` is a `[method_name, metadata_array, proc]` triple
/// produced by the Ruby service class. Constructs an owned service instance,
/// registers all handlers (acquiring GVL for each Ruby proc call), then invokes
/// the entrypoint.
///
/// This function runs on a Ruby thread (entered via function! macro from init), so the GVL is already held.
pub fn {{ fn_name }}({{ fn_param_sig }}) -> magnus::error::Result<()> {
    let mut owner = {{ ctor_call }};

    let ruby = Ruby::get().expect("function! macro callbacks run on a Ruby thread");

    let regs_array = RArray::try_convert(registrations)
        .map_err(|e| magnus::Error::new(ruby.exception_type_error(), e.to_string()))?;

    for i in 0..regs_array.len() {
        let entry = regs_array
            .entry::<Value>(i as isize)
            .map_err(|e| magnus::Error::new(ruby.exception_type_error(), e.to_string()))?;
        let entry_array = RArray::try_convert(entry)
            .map_err(|e| magnus::Error::new(ruby.exception_type_error(), e.to_string()))?;
        let method_name: String = entry_array
            .entry::<String>(0 as isize)
            .map_err(|e| magnus::Error::new(ruby.exception_type_error(), e.to_string()))?;
        let proc_value = entry_array
            .entry::<Value>(2 as isize)
            .map_err(|e| magnus::Error::new(ruby.exception_type_error(), e.to_string()))?;

        match method_name.as_str() {
